IScreenImpl.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Threading.Tasks;
  5. using Avalonia.Metadata;
  6. using Avalonia.Threading;
  7. #pragma warning disable CS1591 // Private API doesn't require XML documentation.
  8. namespace Avalonia.Platform
  9. {
  10. [Unstable]
  11. public interface IScreenImpl
  12. {
  13. int ScreenCount { get; }
  14. IReadOnlyList<Screen> AllScreens { get; }
  15. Action? Changed { get; set; }
  16. Screen? ScreenFromWindow(IWindowBaseImpl window);
  17. Screen? ScreenFromTopLevel(ITopLevelImpl topLevel);
  18. Screen? ScreenFromPoint(PixelPoint point);
  19. Screen? ScreenFromRect(PixelRect rect);
  20. Task<bool> RequestScreenDetails();
  21. }
  22. [PrivateApi]
  23. public class PlatformScreen(IPlatformHandle platformHandle) : Screen
  24. {
  25. public override IPlatformHandle? TryGetPlatformHandle() => platformHandle;
  26. public override int GetHashCode() => platformHandle.GetHashCode();
  27. public override bool Equals(object? obj)
  28. {
  29. return obj is PlatformScreen other && platformHandle.Equals(other.TryGetPlatformHandle()!);
  30. }
  31. }
  32. [PrivateApi]
  33. public abstract class ScreensBase<TKey, TScreen>(IEqualityComparer<TKey>? screenKeyComparer) : IScreenImpl
  34. where TKey : notnull
  35. where TScreen : PlatformScreen
  36. {
  37. private readonly Dictionary<TKey, TScreen> _allScreensByKey = screenKeyComparer is not null ?
  38. new Dictionary<TKey, TScreen>(screenKeyComparer) :
  39. new Dictionary<TKey, TScreen>();
  40. private TScreen[]? _allScreens;
  41. private int? _screenCount;
  42. private bool? _screenDetailsRequestGranted;
  43. private DispatcherOperation? _onChangeOperation;
  44. protected ScreensBase() : this(null)
  45. {
  46. }
  47. public int ScreenCount => _screenCount ??= GetScreenCount();
  48. public IReadOnlyList<Screen> AllScreens
  49. {
  50. get
  51. {
  52. EnsureScreens();
  53. return _allScreens;
  54. }
  55. }
  56. public Action? Changed { get; set; }
  57. public Screen? ScreenFromWindow(IWindowBaseImpl window) => ScreenFromTopLevel(window);
  58. public Screen? ScreenFromTopLevel(ITopLevelImpl topLevel) => ScreenFromTopLevelCore(topLevel);
  59. public Screen? ScreenFromPoint(PixelPoint point) => ScreenFromPointCore(point);
  60. public Screen? ScreenFromRect(PixelRect rect) => ScreenFromRectCore(rect);
  61. public void OnChanged()
  62. {
  63. // Mark cached fields invalid.
  64. _screenCount = null;
  65. _allScreens = null;
  66. // Schedule a delayed job, so we can accumulate multiple continuous events into one.
  67. // Also, if OnChanged was raises on non-UI thread - dispatch it.
  68. _onChangeOperation?.Abort();
  69. _onChangeOperation = Dispatcher.UIThread.InvokeAsync(() =>
  70. {
  71. // Ensure screens if there is at least one subscriber already,
  72. // Or at least one screen was previously materialized, which we need to update now.
  73. if (Changed is not null || _allScreensByKey.Count > 0)
  74. {
  75. EnsureScreens();
  76. Changed?.Invoke();
  77. }
  78. }, DispatcherPriority.Input);
  79. }
  80. public async Task<bool> RequestScreenDetails()
  81. {
  82. _screenDetailsRequestGranted ??= await RequestScreenDetailsCore();
  83. return _screenDetailsRequestGranted.Value;
  84. }
  85. protected bool TryGetScreen(TKey key, [MaybeNullWhen(false)] out TScreen screen)
  86. {
  87. EnsureScreens();
  88. return _allScreensByKey.TryGetValue(key, out screen);
  89. }
  90. protected virtual void ScreenAdded(TScreen screen) => ScreenChanged(screen);
  91. protected virtual void ScreenChanged(TScreen screen) {}
  92. protected virtual void ScreenRemoved(TScreen screen) => screen.OnRemoved();
  93. protected virtual int GetScreenCount() => AllScreens.Count;
  94. protected abstract IReadOnlyList<TKey> GetAllScreenKeys();
  95. protected abstract TScreen CreateScreenFromKey(TKey key);
  96. protected virtual Task<bool> RequestScreenDetailsCore() => Task.FromResult(true);
  97. protected virtual Screen? ScreenFromTopLevelCore(ITopLevelImpl topLevel)
  98. {
  99. if (topLevel is IWindowImpl window)
  100. {
  101. return ScreenHelper.ScreenFromWindow(window, AllScreens);
  102. }
  103. return null;
  104. }
  105. protected virtual Screen? ScreenFromPointCore(PixelPoint point) => ScreenHelper.ScreenFromPoint(point, AllScreens);
  106. protected virtual Screen? ScreenFromRectCore(PixelRect rect) => ScreenHelper.ScreenFromRect(rect, AllScreens);
  107. [MemberNotNull(nameof(_allScreens))]
  108. private void EnsureScreens()
  109. {
  110. if (_allScreens is not null)
  111. return;
  112. var screens = GetAllScreenKeys();
  113. var screensSet = new HashSet<TKey>(screens, screenKeyComparer);
  114. _allScreens = new TScreen[screens.Count];
  115. foreach (var oldScreenKey in _allScreensByKey.Keys)
  116. {
  117. if (!screensSet.Contains(oldScreenKey))
  118. {
  119. if (_allScreensByKey.TryGetValue(oldScreenKey, out var screen)
  120. && _allScreensByKey.Remove(oldScreenKey))
  121. {
  122. ScreenRemoved(screen);
  123. }
  124. }
  125. }
  126. int i = 0;
  127. foreach (var newScreenKey in screens)
  128. {
  129. if (_allScreensByKey.TryGetValue(newScreenKey, out var oldScreen))
  130. {
  131. ScreenChanged(oldScreen);
  132. _allScreens[i] = oldScreen;
  133. }
  134. else
  135. {
  136. var newScreen = CreateScreenFromKey(newScreenKey);
  137. ScreenAdded(newScreen);
  138. _allScreensByKey[newScreenKey] = newScreen;
  139. _allScreens[i] = newScreen;
  140. }
  141. i++;
  142. }
  143. }
  144. }
  145. }