Blizzard_MapCanvas/Blizzard_MapCanvas.lua

Blizzard_MapCanvas/Blizzard_MapCanvas.lua (9.0.2.37474; unchanged since 9.0.2.36665)
  1. MapCanvasMixin = CreateFromMixins(CallbackRegistryMixin);
  2. MapCanvasMixin.MouseAction = { Up = 1, Down = 2, Click = 3 };
  3. function MapCanvasMixin:OnLoad()
  4. CallbackRegistryMixin.OnLoad(self);
  5. self:SetUndefinedEventsAllowed(true);
  6. self.detailLayerPool = CreateFramePool("FRAME", self:GetCanvas(), "MapCanvasDetailLayerTemplate");
  7. self.dataProviders = {};
  8. self.dataProviderEventsCount = {};
  9. self.pinPools = {};
  10. self.pinTemplateTypes = {};
  11. self.activeAreaTriggers = {};
  12. self.lockReasons = {};
  13. self.pinsToNudge = {};
  14. self.pinFrameLevelsManager = CreateFromMixins(MapCanvasPinFrameLevelsManagerMixin);
  15. self.pinFrameLevelsManager:Initialize();
  16. self.mouseClickHandlers = {};
  17. self.globalPinMouseActionHandlers = {};
  18. self.cursorHandlers = {};
  19. self:EvaluateLockReasons();
  20. self.debugAreaTriggers = false;
  21. end
  22. function MapCanvasMixin:OnUpdate()
  23. self:UpdatePinNudging();
  24. self:ProcessCursorHandlers();
  25. end
  26. function MapCanvasMixin:SetMapID(mapID)
  27. if Kiosk.IsEnabled() and KioskFrame:HasWhitelistedMaps() then
  28. local mapIDs = KioskFrame:GetWhitelistedMapIDs();
  29. if not tContains(mapIDs, mapID) then
  30. if not self.mapID then
  31. -- Initialize to an allowed map and assert. Using whitelisted maps is only
  32. -- suitable if we know exactly the maps the player should be in.
  33. assert(false, "Map ID "..mapID.." is not amongst the whitelisted maps.");
  34. mapID = mapIDs[1];
  35. else
  36. -- Not in our list, so don't change the map.
  37. return;
  38. end;
  39. end
  40. end
  41. local mapArtID = C_Map.GetMapArtID(mapID) -- phased map art may be different for the same mapID
  42. if self.mapID ~= mapID or self.mapArtID ~= mapArtID then
  43. self.areDetailLayersDirty = true;
  44. self.mapID = mapID;
  45. self.mapArtID = mapArtID;
  46. self.expandedMapInsetsByMapID = {};
  47. self.ScrollContainer:SetMapID(mapID);
  48. if self:IsShown() then
  49. self:RefreshDetailLayers();
  50. end
  51. self:OnMapChanged();
  52. end
  53. end
  54. function MapCanvasMixin:OnFrameSizeChanged()
  55. self.ScrollContainer:OnCanvasSizeChanged();
  56. end
  57. function MapCanvasMixin:GetMapID()
  58. -- normally the mapID is set in OnShow, however if the player has never opened the quest log or the map, and then
  59. -- hides the UI, and while the UI is hidden opens the quest log, mapID will be nil and we get a lua error.
  60. -- under these very rare circumstances, dig out the diplayable mapID.
  61. return self.mapID or MapUtil.GetDisplayableMapForPlayer();
  62. end
  63. function MapCanvasMixin:SetMapInsetPool(mapInsetPool)
  64. self.mapInsetPool = mapInsetPool;
  65. end
  66. function MapCanvasMixin:GetMapInsetPool()
  67. return self.mapInsetPool;
  68. end
  69. function MapCanvasMixin:OnShow()
  70. local FROM_ON_SHOW = true;
  71. self:RefreshAll(FROM_ON_SHOW);
  72. for dataProvider in pairs(self.dataProviders) do
  73. dataProvider:OnShow();
  74. end
  75. end
  76. function MapCanvasMixin:OnHide()
  77. for dataProvider in pairs(self.dataProviders) do
  78. dataProvider:OnHide();
  79. end
  80. end
  81. function MapCanvasMixin:OnEvent(event, ...)
  82. -- Data provider event
  83. for dataProvider in pairs(self.dataProviders) do
  84. dataProvider:SignalEvent(event, ...);
  85. end
  86. end
  87. function MapCanvasMixin:AddDataProvider(dataProvider)
  88. self.dataProviders[dataProvider] = true;
  89. dataProvider:OnAdded(self);
  90. end
  91. function MapCanvasMixin:RemoveDataProvider(dataProvider)
  92. dataProvider:RemoveAllData();
  93. self.dataProviders[dataProvider] = nil;
  94. dataProvider:OnRemoved(self);
  95. end
  96. function MapCanvasMixin:AddDataProviderEvent(event)
  97. self.dataProviderEventsCount[event] = (self.dataProviderEventsCount[event] or 0) + 1;
  98. self:RegisterEvent(event);
  99. end
  100. function MapCanvasMixin:RemoveDataProviderEvent(event)
  101. if self.dataProviderEventsCount[event] then
  102. self.dataProviderEventsCount[event] = self.dataProviderEventsCount[event] - 1;
  103. if self.dataProviderEventsCount[event] == 0 then
  104. self.dataProviderEventsCount[event] = nil;
  105. self:UnregisterEvent(event);
  106. end
  107. end
  108. end
  109. function MapCanvasMixin:SetPinNudgingDirty(dirty)
  110. self.pinNudgingDirty = dirty;
  111. end
  112. do
  113. local function OnPinReleased(pinPool, pin)
  114. FramePool_HideAndClearAnchors(pinPool, pin);
  115. pin:OnReleased();
  116. pin.pinTemplate = nil;
  117. pin.owningMap = nil;
  118. end
  119. local function OnPinMouseUp(pin, button, upInside)
  120. pin:OnMouseUp(button);
  121. if upInside then
  122. pin:OnClick(button);
  123. end
  124. end
  125. function MapCanvasMixin:AcquirePin(pinTemplate, ...)
  126. if not self.pinPools[pinTemplate] then
  127. local pinTemplateType = self.pinTemplateTypes[pinTemplate] or "FRAME";
  128. self.pinPools[pinTemplate] = CreateFramePool(pinTemplateType, self:GetCanvas(), pinTemplate, OnPinReleased);
  129. end
  130. local pin, newPin = self.pinPools[pinTemplate]:Acquire();
  131. if newPin then
  132. local isMouseClickEnabled = pin:IsMouseClickEnabled();
  133. local isMouseMotionEnabled = pin:IsMouseMotionEnabled();
  134. if isMouseClickEnabled then
  135. pin:SetScript("OnMouseUp", OnPinMouseUp);
  136. pin:SetScript("OnMouseDown", pin.OnMouseDown);
  137. end
  138. if isMouseMotionEnabled then
  139. if newPin then
  140. -- These will never be called, just define a OnMouseEnter and OnMouseLeave on the pin mixin and it'll be called when appropriate
  141. assert(pin:GetScript("OnEnter") == nil);
  142. assert(pin:GetScript("OnLeave") == nil);
  143. end
  144. pin:SetScript("OnEnter", pin.OnMouseEnter);
  145. pin:SetScript("OnLeave", pin.OnMouseLeave);
  146. end
  147. pin:SetMouseClickEnabled(isMouseClickEnabled);
  148. pin:SetMouseMotionEnabled(isMouseMotionEnabled);
  149. end
  150. pin.pinTemplate = pinTemplate;
  151. pin.owningMap = self;
  152. if newPin then
  153. pin:OnLoad();
  154. end
  155. self.ScrollContainer:MarkCanvasDirty();
  156. pin:Show();
  157. pin:OnAcquired(...);
  158. return pin;
  159. end
  160. end
  161. function MapCanvasMixin:SetPinTemplateType(pinTemplate, pinTemplateType)
  162. self.pinTemplateTypes[pinTemplate] = pinTemplateType;
  163. end
  164. function MapCanvasMixin:RemoveAllPinsByTemplate(pinTemplate)
  165. if self.pinPools[pinTemplate] then
  166. self.pinPools[pinTemplate]:ReleaseAll();
  167. self.ScrollContainer:MarkCanvasDirty();
  168. end
  169. end
  170. function MapCanvasMixin:RemovePin(pin)
  171. if pin:GetNudgeSourceRadius() > 0 then
  172. self.pinNudgingDirty = true;
  173. end
  174. self.pinPools[pin.pinTemplate]:Release(pin);
  175. self.ScrollContainer:MarkCanvasDirty();
  176. end
  177. function MapCanvasMixin:EnumeratePinsByTemplate(pinTemplate)
  178. if self.pinPools[pinTemplate] then
  179. return self.pinPools[pinTemplate]:EnumerateActive();
  180. end
  181. return nop;
  182. end
  183. function MapCanvasMixin:GetNumActivePinsByTemplate(pinTemplate)
  184. if self.pinPools[pinTemplate] then
  185. return self.pinPools[pinTemplate]:GetNumActive();
  186. end
  187. return 0;
  188. end
  189. function MapCanvasMixin:EnumerateAllPins()
  190. local currentPoolKey, currentPool = next(self.pinPools, nil);
  191. local currentPin = nil;
  192. return function()
  193. if currentPool then
  194. currentPin = currentPool:GetNextActive(currentPin);
  195. while not currentPin do
  196. currentPoolKey, currentPool = next(self.pinPools, currentPoolKey);
  197. if currentPool then
  198. currentPin = currentPool:GetNextActive();
  199. else
  200. break;
  201. end
  202. end
  203. end
  204. return currentPin;
  205. end, nil;
  206. end
  207. function MapCanvasMixin:AcquireAreaTrigger(namespace)
  208. if not self.activeAreaTriggers[namespace] then
  209. self.activeAreaTriggers[namespace] = {};
  210. end
  211. local areaTrigger = CreateRectangle();
  212. areaTrigger.enclosed = false;
  213. areaTrigger.intersects = false;
  214. areaTrigger.intersectCallback = nil;
  215. areaTrigger.enclosedCallback = nil;
  216. areaTrigger.triggerPredicate = nil;
  217. self.activeAreaTriggers[namespace][areaTrigger] = true;
  218. self.ScrollContainer:MarkAreaTriggersDirty();
  219. return areaTrigger;
  220. end
  221. function MapCanvasMixin:SetAreaTriggerEnclosedCallback(areaTrigger, enclosedCallback)
  222. areaTrigger.enclosedCallback = enclosedCallback;
  223. self.ScrollContainer:MarkAreaTriggersDirty();
  224. end
  225. function MapCanvasMixin:SetAreaTriggerIntersectsCallback(areaTrigger, intersectCallback)
  226. areaTrigger.intersectCallback = intersectCallback;
  227. self.ScrollContainer:MarkAreaTriggersDirty();
  228. end
  229. function MapCanvasMixin:SetAreaTriggerPredicate(areaTrigger, triggerPredicate)
  230. areaTrigger.triggerPredicate = triggerPredicate;
  231. self.ScrollContainer:MarkAreaTriggersDirty();
  232. end
  233. function MapCanvasMixin:ReleaseAreaTriggers(namespace)
  234. self.activeAreaTriggers[namespace] = nil;
  235. self:TryRefreshingDebugAreaTriggers();
  236. end
  237. function MapCanvasMixin:ReleaseAreaTrigger(namespace, areaTrigger)
  238. if self.activeAreaTriggers[namespace] then
  239. self.activeAreaTriggers[namespace][areaTrigger] = nil;
  240. self:TryRefreshingDebugAreaTriggers();
  241. end
  242. end
  243. function MapCanvasMixin:UpdateAreaTriggers(scrollRect)
  244. for namespace, areaTriggers in pairs(self.activeAreaTriggers) do
  245. for areaTrigger in pairs(areaTriggers) do
  246. if areaTrigger.intersectCallback then
  247. local intersects = (not areaTrigger.triggerPredicate or areaTrigger.triggerPredicate(areaTrigger)) and scrollRect:IntersectsRect(areaTrigger);
  248. if areaTrigger.intersects ~= intersects then
  249. areaTrigger.intersects = intersects;
  250. areaTrigger.intersectCallback(areaTrigger, intersects);
  251. end
  252. end
  253. if areaTrigger.enclosedCallback then
  254. local enclosed = (not areaTrigger.triggerPredicate or areaTrigger.triggerPredicate(areaTrigger)) and scrollRect:EnclosesRect(areaTrigger);
  255. if areaTrigger.enclosed ~= enclosed then
  256. areaTrigger.enclosed = enclosed;
  257. areaTrigger.enclosedCallback(areaTrigger, enclosed);
  258. end
  259. end
  260. end
  261. end
  262. self:TryRefreshingDebugAreaTriggers();
  263. end
  264. function SquaredDistanceBetweenPoints(firstX, firstY, secondX, secondY)
  265. local xDiff = firstX - secondX;
  266. local yDiff = firstY - secondY;
  267. return xDiff * xDiff + yDiff * yDiff;
  268. end
  269. function MapCanvasMixin:CalculatePinNudging(targetPin)
  270. targetPin:SetNudgeVector(nil, nil, nil, nil);
  271. if not targetPin:IgnoresNudging() and targetPin:GetNudgeTargetFactor() > 0 then
  272. local normalizedX, normalizedY = targetPin:GetPosition();
  273. for sourcePin in self:EnumerateAllPins() do
  274. if targetPin ~= sourcePin and not sourcePin:IgnoresNudging() and sourcePin:GetNudgeSourceRadius() > 0 then
  275. local otherNormalizedX, otherNormalizedY = sourcePin:GetPosition();
  276. local distanceSquared = SquaredDistanceBetweenPoints(normalizedX, normalizedY, otherNormalizedX, otherNormalizedY);
  277. local nudgeFactor = targetPin:GetNudgeTargetFactor() * sourcePin:GetNudgeSourceRadius();
  278. if distanceSquared < nudgeFactor * nudgeFactor then
  279. local distance = math.sqrt(distanceSquared);
  280. -- Avoid divide by zero: just push it right.
  281. if distanceSquared == 0 then
  282. targetPin:SetNudgeVector(sourcePin:GetNudgeSourceZoomedOutMagnitude(), sourcePin:GetNudgeSourceZoomedInMagnitude(), 1, 0);
  283. else
  284. targetPin:SetNudgeVector(sourcePin:GetNudgeSourceZoomedOutMagnitude(), sourcePin:GetNudgeSourceZoomedInMagnitude(), (normalizedX - otherNormalizedX) / distance, (normalizedY - otherNormalizedY) / distance);
  285. end
  286. targetPin:SetNudgeFactor(1 - (distance / nudgeFactor));
  287. break; -- This is non-exact: each target pin only gets pushed by one source pin.
  288. end
  289. end
  290. end
  291. end
  292. end
  293. function MapCanvasMixin:UpdatePinNudging()
  294. if not self.pinNudgingDirty and #self.pinsToNudge == 0 then
  295. return;
  296. end
  297. if self.pinNudgingDirty then
  298. for targetPin in self:EnumerateAllPins() do
  299. self:CalculatePinNudging(targetPin);
  300. end
  301. else
  302. for _, targetPin in ipairs(self.pinsToNudge) do
  303. self:CalculatePinNudging(targetPin);
  304. end
  305. end
  306. self.pinNudgingDirty = false;
  307. self.pinsToNudge = {};
  308. end
  309. function MapCanvasMixin:TryRefreshingDebugAreaTriggers()
  310. if self.debugAreaTriggers then
  311. self:RefreshDebugAreaTriggers();
  312. elseif self.debugAreaTriggerPool then
  313. self.debugAreaTriggerPool:ReleaseAll();
  314. end
  315. end
  316. function MapCanvasMixin:RefreshDebugAreaTriggers()
  317. if not self.debugAreaTriggerPool then
  318. self.debugAreaTriggerPool = CreateTexturePool(self:GetCanvas(), "OVERLAY", 7, "MapCanvasDebugTriggerAreaTemplate");
  319. self.debugAreaTriggerColors = {};
  320. end
  321. self.debugAreaTriggerPool:ReleaseAll();
  322. local canvas = self:GetCanvas();
  323. for namespace, areaTriggers in pairs(self.activeAreaTriggers) do
  324. if not self.debugAreaTriggerColors[namespace] then
  325. self.debugAreaTriggerColors[namespace] = { math.random(), math.random(), math.random(), 0.45 };
  326. end
  327. for areaTrigger in pairs(areaTriggers) do
  328. local debugAreaTexture = self.debugAreaTriggerPool:Acquire();
  329. debugAreaTexture:SetPoint("TOPLEFT", canvas, "TOPLEFT", canvas:GetWidth() * areaTrigger:GetLeft(), -canvas:GetHeight() * areaTrigger:GetTop());
  330. debugAreaTexture:SetPoint("BOTTOMRIGHT", canvas, "TOPLEFT", canvas:GetWidth() * areaTrigger:GetRight(), -canvas:GetHeight() * areaTrigger:GetBottom());
  331. debugAreaTexture:SetColorTexture(unpack(self.debugAreaTriggerColors[namespace]));
  332. debugAreaTexture:Show();
  333. end
  334. end
  335. end
  336. function MapCanvasMixin:SetDebugAreaTriggersEnabled(enabled)
  337. self.debugAreaTriggers = enabled;
  338. self.ScrollContainer:MarkAreaTriggersDirty();
  339. end
  340. function MapCanvasMixin:ForceRefreshDetailLayers()
  341. self.areDetailLayersDirty = true;
  342. self:RefreshDetailLayers();
  343. end
  344. function MapCanvasMixin:RefreshDetailLayers()
  345. if not self.areDetailLayersDirty then return end;
  346. self.detailLayerPool:ReleaseAll();
  347. local layers = C_Map.GetMapArtLayers(self.mapID);
  348. for layerIndex, layerInfo in ipairs(layers) do
  349. local detailLayer = self.detailLayerPool:Acquire();
  350. detailLayer:SetAllPoints(self:GetCanvas());
  351. detailLayer:SetMapAndLayer(self.mapID, layerIndex, self);
  352. detailLayer:SetGlobalAlpha(self:GetGlobalAlpha());
  353. detailLayer:Show();
  354. end
  355. self:AdjustDetailLayerAlpha();
  356. self.areDetailLayersDirty = false;
  357. end
  358. function MapCanvasMixin:AreDetailLayersLoaded()
  359. for detailLayer in self.detailLayerPool:EnumerateActive() do
  360. if not detailLayer:IsFullyLoaded() then
  361. return false;
  362. end
  363. end
  364. return true;
  365. end
  366. function MapCanvasMixin:AdjustDetailLayerAlpha()
  367. self.ScrollContainer:AdjustDetailLayerAlpha(self.detailLayerPool);
  368. end
  369. function MapCanvasMixin:RefreshAllDataProviders(fromOnShow)
  370. for dataProvider in pairs(self.dataProviders) do
  371. dataProvider:RefreshAllData(fromOnShow);
  372. end
  373. end
  374. function MapCanvasMixin:ResetInsets()
  375. if self.mapInsetPool then
  376. self.mapInsetPool:ReleaseAll();
  377. self.mapInsetsByIndex = {};
  378. end
  379. end
  380. function MapCanvasMixin:RefreshInsets()
  381. self:ResetInsets();
  382. end
  383. function MapCanvasMixin:AddInset(insetIndex, mapID, title, description, collapsedIcon, numDetailTiles, normalizedX, normalizedY)
  384. if self.mapInsetPool then
  385. local mapInset = self.mapInsetPool:Acquire();
  386. local expanded = self.expandedMapInsetsByMapID[mapID];
  387. mapInset:Initialize(self, not expanded, insetIndex, mapID, title, description, collapsedIcon, numDetailTiles, normalizedX, normalizedY);
  388. self.mapInsetsByIndex[insetIndex] = mapInset;
  389. end
  390. end
  391. function MapCanvasMixin:RefreshAll(fromOnShow)
  392. self:RefreshDetailLayers();
  393. self:RefreshInsets();
  394. self:RefreshAllDataProviders(fromOnShow);
  395. end
  396. function MapCanvasMixin:SetPinPosition(pin, normalizedX, normalizedY, insetIndex)
  397. self:ApplyPinPosition(pin, normalizedX, normalizedY, insetIndex);
  398. if not pin:IgnoresNudging() then
  399. if pin:GetNudgeSourceRadius() > 0 then
  400. -- If we nudge other things we need to recalculate all nudging.
  401. self.pinNudgingDirty = true;
  402. else
  403. self.pinsToNudge[#self.pinsToNudge + 1] = pin;
  404. end
  405. end
  406. end
  407. function MapCanvasMixin:ApplyPinPosition(pin, normalizedX, normalizedY, insetIndex)
  408. if insetIndex then
  409. if self.mapInsetsByIndex and self.mapInsetsByIndex[insetIndex] then
  410. self.mapInsetsByIndex[insetIndex]:SetLocalPinPosition(pin, normalizedX, normalizedY);
  411. pin:ApplyFrameLevel();
  412. end
  413. else
  414. pin:ClearAllPoints();
  415. if normalizedX and normalizedY then
  416. local x = normalizedX;
  417. local y = normalizedY;
  418. local nudgeVectorX, nudgeVectorY = pin:GetNudgeVector();
  419. if nudgeVectorX and nudgeVectorY then
  420. local finalNudgeFactor = pin:GetNudgeFactor() * pin:GetNudgeTargetFactor() * pin:GetNudgeZoomFactor();
  421. x = normalizedX + nudgeVectorX * finalNudgeFactor;
  422. y = normalizedY + nudgeVectorY * finalNudgeFactor;
  423. end
  424. local canvas = self:GetCanvas();
  425. local scale = pin:GetScale();
  426. pin:SetParent(canvas);
  427. pin:ApplyFrameLevel();
  428. pin:SetPoint("CENTER", canvas, "TOPLEFT", (canvas:GetWidth() * x) / scale, -(canvas:GetHeight() * y) / scale);
  429. end
  430. end
  431. end
  432. function MapCanvasMixin:GetGlobalPosition(normalizedX, normalizedY, insetIndex)
  433. if self.mapInsetsByIndex and self.mapInsetsByIndex[insetIndex] then
  434. return self.mapInsetsByIndex[insetIndex]:GetGlobalPosition(normalizedX, normalizedY);
  435. end
  436. return normalizedX, normalizedY;
  437. end
  438. function MapCanvasMixin:GetCanvas()
  439. return self.ScrollContainer.Child;
  440. end
  441. function MapCanvasMixin:GetCanvasContainer()
  442. return self.ScrollContainer;
  443. end
  444. function MapCanvasMixin:CallMethodOnPinsAndDataProviders(methodName, ...)
  445. for dataProvider in pairs(self.dataProviders) do
  446. dataProvider[methodName](dataProvider, ...);
  447. end
  448. for pin in self:EnumerateAllPins() do
  449. pin[methodName](pin, ...);
  450. end
  451. end
  452. function MapCanvasMixin:OnMapInsetSizeChanged(mapID, mapInsetIndex, expanded)
  453. self.expandedMapInsetsByMapID[mapID] = expanded;
  454. self:CallMethodOnPinsAndDataProviders("OnMapInsetSizeChanged", mapInsetIndex, expanded);
  455. end
  456. function MapCanvasMixin:OnMapInsetMouseEnter(mapInsetIndex)
  457. self:CallMethodOnPinsAndDataProviders("OnMapInsetMouseEnter", mapInsetIndex);
  458. end
  459. function MapCanvasMixin:OnMapInsetMouseLeave(mapInsetIndex)
  460. self:CallMethodOnPinsAndDataProviders("OnMapInsetMouseLeave", mapInsetIndex);
  461. end
  462. function MapCanvasMixin:OnMapChanged()
  463. for dataProvider in pairs(self.dataProviders) do
  464. dataProvider:OnMapChanged();
  465. end
  466. end
  467. function MapCanvasMixin:OnCanvasScaleChanged()
  468. self:AdjustDetailLayerAlpha();
  469. if self.mapInsetsByIndex then
  470. for insetIndex, mapInset in pairs(self.mapInsetsByIndex) do
  471. mapInset:OnCanvasScaleChanged();
  472. end
  473. end
  474. self:CallMethodOnPinsAndDataProviders("OnCanvasScaleChanged");
  475. end
  476. function MapCanvasMixin:OnCanvasPanChanged()
  477. self:CallMethodOnPinsAndDataProviders("OnCanvasPanChanged");
  478. end
  479. function MapCanvasMixin:OnCanvasSizeChanged()
  480. self:CallMethodOnPinsAndDataProviders("OnCanvasSizeChanged");
  481. end
  482. function MapCanvasMixin:GetCanvasScale()
  483. return self.ScrollContainer:GetCanvasScale();
  484. end
  485. function MapCanvasMixin:GetCanvasZoomPercent()
  486. return self.ScrollContainer:GetCanvasZoomPercent();
  487. end
  488. function MapCanvasMixin:IsZoomingIn()
  489. return self.ScrollContainer:IsZoomingIn();
  490. end
  491. function MapCanvasMixin:IsZoomingOut()
  492. return self.ScrollContainer:IsZoomingOut();
  493. end
  494. function MapCanvasMixin:ZoomIn()
  495. self.ScrollContainer:ZoomIn();
  496. end
  497. function MapCanvasMixin:ZoomOut()
  498. self.ScrollContainer:ZoomOut();
  499. end
  500. function MapCanvasMixin:ResetZoom()
  501. self.ScrollContainer:ResetZoom();
  502. end
  503. function MapCanvasMixin:InstantPanAndZoom(scale, x, y, ignoreScaleRatio)
  504. self.ScrollContainer:InstantPanAndZoom(scale, x, y, ignoreScaleRatio);
  505. end
  506. function MapCanvasMixin:IsAtMaxZoom()
  507. return self.ScrollContainer:IsAtMaxZoom();
  508. end
  509. function MapCanvasMixin:IsAtMinZoom()
  510. return self.ScrollContainer:IsAtMinZoom();
  511. end
  512. function MapCanvasMixin:PanTo(normalizedX, normalizedY)
  513. self.ScrollContainer:SetPanTarget(normalizedX, normalizedY);
  514. end
  515. function MapCanvasMixin:PanAndZoomTo(normalizedX, normalizedY)
  516. self.ScrollContainer:SetPanTarget(normalizedX, normalizedY);
  517. self.ScrollContainer:ZoomIn();
  518. end
  519. function MapCanvasMixin:SetMouseWheelZoomMode(zoomMode)
  520. self.ScrollContainer:SetMouseWheelZoomMode(zoomMode);
  521. end
  522. function MapCanvasMixin:SetShouldZoomInOnClick(shouldZoomInOnClick)
  523. self.ScrollContainer:SetShouldZoomInOnClick(shouldZoomInOnClick);
  524. end
  525. function MapCanvasMixin:ShouldZoomInOnClick()
  526. return self.ScrollContainer:ShouldZoomInOnClick();
  527. end
  528. function MapCanvasMixin:SetShouldNavigateOnClick(shouldNavigateOnClick)
  529. self.ScrollContainer:SetShouldNavigateOnClick(shouldNavigateOnClick);
  530. end
  531. function MapCanvasMixin:ShouldNavigateOnClick()
  532. return self.ScrollContainer:ShouldNavigateOnClick();
  533. end
  534. function MapCanvasMixin:SetShouldPanOnClick(shouldPanOnClick)
  535. self.ScrollContainer:SetShouldPanOnClick(shouldPanOnClick);
  536. end
  537. function MapCanvasMixin:ShouldPanOnClick()
  538. return self.ScrollContainer:ShouldPanOnClick();
  539. end
  540. function MapCanvasMixin:SetShouldZoomInstantly(shouldZoomInstantly)
  541. self.ScrollContainer:SetShouldZoomInstantly(shouldZoomInstantly);
  542. end
  543. function MapCanvasMixin:ShouldZoomInstantly()
  544. return self.ScrollContainer:ShouldZoomInstantly();
  545. end
  546. function MapCanvasMixin:GetViewRect()
  547. return self.ScrollContainer:GetViewRect();
  548. end
  549. function MapCanvasMixin:GetMaxZoomViewRect()
  550. return self.ScrollContainer:GetMaxZoomViewRect();
  551. end
  552. function MapCanvasMixin:GetMinZoomViewRect()
  553. return self.ScrollContainer:GetMinZoomViewRect();
  554. end
  555. function MapCanvasMixin:GetScaleForMaxZoom()
  556. return self.ScrollContainer:GetScaleForMaxZoom();
  557. end
  558. function MapCanvasMixin:GetScaleForMinZoom()
  559. return self.ScrollContainer:GetScaleForMinZoom();
  560. end
  561. function MapCanvasMixin:CalculateZoomScaleAndPositionForAreaInViewRect(...)
  562. return self.ScrollContainer:CalculateZoomScaleAndPositionForAreaInViewRect(...);
  563. end
  564. function MapCanvasMixin:NormalizeHorizontalSize(size)
  565. return self.ScrollContainer:NormalizeHorizontalSize(size);
  566. end
  567. function MapCanvasMixin:DenormalizeHorizontalSize(size)
  568. return self.ScrollContainer:DenormalizeHorizontalSize(size);
  569. end
  570. function MapCanvasMixin:NormalizeVerticalSize(size)
  571. return self.ScrollContainer:NormalizeVerticalSize(size);
  572. end
  573. function MapCanvasMixin:DenormalizeVerticalSize(size)
  574. return self.ScrollContainer:DenormalizeVerticalSize(size);
  575. end
  576. function MapCanvasMixin:GetNormalizedCursorPosition()
  577. return self.ScrollContainer:GetNormalizedCursorPosition()
  578. end
  579. function MapCanvasMixin:IsCanvasMouseFocus()
  580. return self.ScrollContainer == GetMouseFocus();
  581. end
  582. function MapCanvasMixin:AddLockReason(reason)
  583. self.lockReasons[reason] = true;
  584. self:EvaluateLockReasons();
  585. end
  586. function MapCanvasMixin:RemoveLockReason(reason)
  587. self.lockReasons[reason] = nil;
  588. self:EvaluateLockReasons();
  589. end
  590. function MapCanvasMixin:EvaluateLockReasons()
  591. if next(self.lockReasons) then
  592. self.BorderFrame:EnableMouse(true);
  593. self.BorderFrame:EnableMouseWheel(true);
  594. if self.BorderFrame.Underlay then
  595. self.BorderFrame.Underlay:Show();
  596. end
  597. else
  598. self.BorderFrame:EnableMouse(false);
  599. self.BorderFrame:EnableMouseWheel(false);
  600. if self.BorderFrame.Underlay then
  601. self.BorderFrame.Underlay:Hide();
  602. end
  603. end
  604. end
  605. function MapCanvasMixin:GetPinFrameLevelsManager()
  606. return self.pinFrameLevelsManager;
  607. end
  608. function MapCanvasMixin:ReapplyPinFrameLevels(pinFrameLevelType)
  609. for pin in self:EnumerateAllPins() do
  610. if pin:GetFrameLevelType() == pinFrameLevelType then
  611. pin:ApplyFrameLevel();
  612. end
  613. end
  614. end
  615. function MapCanvasMixin:NavigateToParentMap()
  616. local mapInfo = C_Map.GetMapInfo(self:GetMapID());
  617. if mapInfo.parentMapID > 0 then
  618. self:SetMapID(mapInfo.parentMapID);
  619. end
  620. end
  621. function MapCanvasMixin:NavigateToCursor()
  622. local normalizedCursorX, normalizedCursorY = self:GetNormalizedCursorPosition();
  623. local mapInfo = C_Map.GetMapInfoAtPosition(self:GetMapID(), normalizedCursorX, normalizedCursorY);
  624. if mapInfo then
  625. self:SetMapID(mapInfo.mapID);
  626. end
  627. end
  628. local function PrioritySorter(left, right)
  629. return left.priority > right.priority;
  630. end
  631. -- Add a function that will be checked when the canvas is clicked
  632. -- If the function returns true then handling will stop
  633. -- A priority can optionally be specified, higher priority values will be called first
  634. function MapCanvasMixin:AddCanvasClickHandler(handler, priority)
  635. table.insert(self.mouseClickHandlers, { handler = handler, priority = priority or 0 });
  636. table.sort(self.mouseClickHandlers, PrioritySorter);
  637. end
  638. function MapCanvasMixin:RemoveCanvasClickHandler(handler, priority)
  639. for i, handlerInfo in ipairs(self.mouseClickHandlers) do
  640. if handlerInfo.handler == handler and (not priority or handlerInfo.priority == priority) then
  641. table.remove(self.mouseClickHandlers, i);
  642. break;
  643. end
  644. end
  645. end
  646. function MapCanvasMixin:ProcessCanvasClickHandlers(button, cursorX, cursorY)
  647. for i, handlerInfo in ipairs(self.mouseClickHandlers) do
  648. local success, stopChecking = xpcall(handlerInfo.handler, CallErrorHandler, self, button, cursorX, cursorY);
  649. if success and stopChecking then
  650. return true;
  651. end
  652. end
  653. return false;
  654. end
  655. -- Add a function that will be checked when any pin is clicked
  656. -- If the function returns true then handling will stop
  657. -- A priority can optionally be specified, higher priority values will be called first
  658. function MapCanvasMixin:AddGlobalPinMouseActionHandler(handler, priority)
  659. table.insert(self.globalPinMouseActionHandlers, { handler = handler, priority = priority or 0 });
  660. table.sort(self.globalPinMouseActionHandlers, PrioritySorter);
  661. end
  662. function MapCanvasMixin:RemoveGlobalPinMouseActionHandler(handler, priority)
  663. for i, handlerInfo in ipairs(self.globalPinMouseActionHandlers) do
  664. if handlerInfo.handler == handler and (not priority or handlerInfo.priority == priority) then
  665. table.remove(self.globalPinMouseActionHandlers, i);
  666. break;
  667. end
  668. end
  669. end
  670. function MapCanvasMixin:ProcessGlobalPinMouseActionHandlers(mouseAction, button)
  671. for i, handlerInfo in ipairs(self.globalPinMouseActionHandlers) do
  672. local success, stopChecking = xpcall(handlerInfo.handler, CallErrorHandler, self, mouseAction, button);
  673. if success and stopChecking then
  674. return true;
  675. end
  676. end
  677. return false;
  678. end
  679. function MapCanvasMixin:AddCursorHandler(handler, priority)
  680. table.insert(self.cursorHandlers, { handler = handler, priority = priority or 0 });
  681. table.sort(self.cursorHandlers, PrioritySorter);
  682. end
  683. function MapCanvasMixin:RemoveCursorHandler(handler, priority)
  684. for i, handlerInfo in ipairs(self.cursorHandlers) do
  685. if handlerInfo.handler == handler and (not priority or handlerInfo.priority == priority) then
  686. table.remove(self.cursorHandlers, i);
  687. break;
  688. end
  689. end
  690. end
  691. function MapCanvasMixin:ProcessCursorHandlers()
  692. local focus = GetMouseFocus();
  693. if focus then
  694. -- pins have a .owningMap, our pins should be pointing to us
  695. if focus == self.ScrollContainer or focus.owningMap == self then
  696. for i, handlerInfo in ipairs(self.cursorHandlers) do
  697. local success, cursor = xpcall(handlerInfo.handler, CallErrorHandler, self);
  698. if success and cursor then
  699. self.lastCursor = cursor;
  700. SetCursor(cursor);
  701. return;
  702. end
  703. end
  704. end
  705. end
  706. if self.lastCursor then
  707. self.lastCursor = nil;
  708. ResetCursor();
  709. end
  710. end
  711. function MapCanvasMixin:GetGlobalPinScale()
  712. return self.globalPinScale or 1;
  713. end
  714. function MapCanvasMixin:SetGlobalPinScale(scale)
  715. if self.globalPinScale ~= scale then
  716. self.globalPinScale = scale;
  717. for pin in self:EnumerateAllPins() do
  718. pin:ApplyCurrentScale();
  719. end
  720. end
  721. end
  722. function MapCanvasMixin:GetGlobalAlpha()
  723. return self.globalAlpha or 1;
  724. end
  725. function MapCanvasMixin:SetGlobalAlpha(globalAlpha)
  726. if self.globalAlpha ~= globalAlpha then
  727. self.globalAlpha = globalAlpha;
  728. for detailLayer in self.detailLayerPool:EnumerateActive() do
  729. detailLayer:SetGlobalAlpha(globalAlpha);
  730. end
  731. for dataProvider in pairs(self.dataProviders) do
  732. dataProvider:OnGlobalAlphaChanged();
  733. end
  734. end
  735. end
  736. function MapCanvasMixin:SetMaskTexture(maskTexture)
  737. if self.maskTexture then
  738. for texture, value in pairs(self.maskableTextures) do
  739. self.maskableTextures[texture] = false;
  740. texture:RemoveMaskTexture(self.maskTexture);
  741. end
  742. end
  743. self.maskTexture = maskTexture;
  744. if self.maskableTextures then
  745. self:RefreshMaskableTextures();
  746. else
  747. self.maskableTextures = { };
  748. end
  749. end
  750. function MapCanvasMixin:GetMaskTexture()
  751. return self.maskTexture;
  752. end
  753. function MapCanvasMixin:SetUseMaskTexture(useMaskTexture)
  754. if not self:GetMaskTexture() then
  755. error("Must have a mask texture");
  756. end
  757. self.useMaskTexture = useMaskTexture;
  758. self:RefreshMaskableTextures();
  759. end
  760. function MapCanvasMixin:GetUseMaskTexture()
  761. return not not self.useMaskTexture;
  762. end
  763. function MapCanvasMixin:AddMaskableTexture(texture)
  764. local maskTexture = self:GetMaskTexture();
  765. if not maskTexture then
  766. return;
  767. end
  768. if self.maskableTextures[texture] ~= nil then
  769. return;
  770. end
  771. local useMaskTexture = self:GetUseMaskTexture();
  772. self.maskableTextures[texture] = useMaskTexture;
  773. if useMaskTexture then
  774. texture:AddMaskTexture(maskTexture);
  775. end
  776. end
  777. function MapCanvasMixin:RefreshMaskableTextures()
  778. local useMaskTexture = self:GetUseMaskTexture();
  779. local maskTexture = self:GetMaskTexture();
  780. for texture, value in pairs(self.maskableTextures) do
  781. if value ~= useMaskTexture then
  782. self.maskableTextures[texture] = useMaskTexture;
  783. if useMaskTexture then
  784. texture:AddMaskTexture(maskTexture);
  785. else
  786. texture:RemoveMaskTexture(maskTexture);
  787. end
  788. end
  789. end
  790. end