|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using ConsoleFramework.Native; |
| 4 | + |
| 5 | +namespace ConsoleFramework |
| 6 | +{ |
| 7 | + /// <summary> |
| 8 | + /// Хранит в себе стек окон, по запросам, приходящим из ConsoleApplication, обрабатывает ввод пользователя, |
| 9 | + /// вычисляя, какому окно был предназначен этот ввод и перенаправляя событие ввода этому окну. |
| 10 | + /// </summary> |
| 11 | + public sealed class ConsoleDispatcher : IElementsVisibilityAware { |
| 12 | + public List<FrameworkElement> WindowsStack = new List<FrameworkElement>(); |
| 13 | + private readonly Dictionary<FrameworkElement, Visibility> visibilityCache = new Dictionary<FrameworkElement, Visibility>(); |
| 14 | + public bool visibilityAnalyzeCompleted = false; |
| 15 | + |
| 16 | + public FrameworkElementVisibility GetElementVisibility(FrameworkElement element) { |
| 17 | + return getElementVisibilityCore(element).Type; |
| 18 | + } |
| 19 | + |
| 20 | + private Visibility getElementVisibilityCore(FrameworkElement element) { |
| 21 | + if (!visibilityAnalyzeCompleted) { |
| 22 | + AnalyzeVisibility(); |
| 23 | + } |
| 24 | + Visibility res; |
| 25 | + if (!visibilityCache.TryGetValue(element, out res)) { |
| 26 | + throw new InvalidOperationException("Element was not found at visibility cache."); |
| 27 | + } |
| 28 | + return res; |
| 29 | + } |
| 30 | + |
| 31 | + public bool IsPointOfElementVisible(int x, int y, FrameworkElement element) { |
| 32 | + if (x < 0 || y < 0 || x > element.Width || y > element.Height) { |
| 33 | + throw new ArgumentException("Coords to point are invalid."); |
| 34 | + } |
| 35 | + // |
| 36 | + Visibility visibility = getElementVisibilityCore(element); |
| 37 | + if (visibility.Type == FrameworkElementVisibility.FullVisible) { |
| 38 | + return true; |
| 39 | + } |
| 40 | + if (visibility.Type == FrameworkElementVisibility.Hidden) { |
| 41 | + return false; |
| 42 | + } |
| 43 | + // |
| 44 | + List<SMALL_RECT> overlappedRegions = visibility.OverlappedRegions; |
| 45 | + foreach (SMALL_RECT rect in overlappedRegions) { |
| 46 | + // если точка лежит внутри одной из областей, перекрытых верхними окнами - false |
| 47 | + if (x >= rect.Left && x <= rect.Right && y >= rect.Top && y <= rect.Bottom) { |
| 48 | + return false; |
| 49 | + } |
| 50 | + } |
| 51 | + return true; |
| 52 | + } |
| 53 | + |
| 54 | + public void DispatchInputEvent(INPUT_RECORD inputRecord) { |
| 55 | + // |
| 56 | + } |
| 57 | + |
| 58 | + public void RegisterWindow(FrameworkElement element) { |
| 59 | + WindowsStack.Add(element); |
| 60 | + if (visibilityAnalyzeCompleted) { |
| 61 | + visibilityAnalyzeCompleted = false; |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + public void AnalyzeVisibility() { |
| 66 | + if (visibilityAnalyzeCompleted) { |
| 67 | + return; |
| 68 | + } |
| 69 | + // |
| 70 | + if (WindowsStack.Count > 0) { |
| 71 | + for (int i = WindowsStack.Count - 1; i >= 0; --i) { |
| 72 | + FrameworkElement element = WindowsStack[i]; |
| 73 | + Visibility visibility; |
| 74 | + if (!visibilityCache.TryGetValue(element, out visibility)) { |
| 75 | + visibility = new Visibility(FrameworkElementVisibility.FullVisible); |
| 76 | + visibilityCache.Add(element, visibility); |
| 77 | + } else { |
| 78 | + visibility.Type = FrameworkElementVisibility.FullVisible; |
| 79 | + visibility.OverlappedRegions.Clear(); |
| 80 | + } |
| 81 | + // |
| 82 | + for (int j = WindowsStack.Count - 1; j > i; --j) { |
| 83 | + FrameworkElement element2 = WindowsStack[j]; |
| 84 | + if (visibilityCache[element2].Type == FrameworkElementVisibility.Hidden) { |
| 85 | + continue; |
| 86 | + } |
| 87 | + SMALL_RECT? overlappingRegion = getOverlappingRegion(element, element2); |
| 88 | + if (overlappingRegion != null) { |
| 89 | + SMALL_RECT rect = overlappingRegion.Value; |
| 90 | + if (rect.Left == 0 && rect.Top == 0 && rect.Right == element.Width && rect.Bottom == element.Height) { |
| 91 | + visibility.Type = FrameworkElementVisibility.Hidden; |
| 92 | + break; |
| 93 | + } |
| 94 | + visibility.OverlappedRegions.Add(rect); |
| 95 | + visibility.Type = FrameworkElementVisibility.PartiallyVisible; |
| 96 | + } |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + visibilityAnalyzeCompleted = true; |
| 101 | + } |
| 102 | + |
| 103 | + /// <summary> |
| 104 | + /// Проверяет, перекрывает ли b, находящийся сверху, элемент a, находящийся под ним. |
| 105 | + /// Возвращает координаты прямоугольной области относительно элемента a, если b его перекрывает. |
| 106 | + /// Или null в случае, если b не перекрывает a вообще. |
| 107 | + /// </summary> |
| 108 | + public static SMALL_RECT? getOverlappingRegion(FrameworkElement a, FrameworkElement b) { |
| 109 | + SMALL_RECT? res = null; |
| 110 | + // |
| 111 | + bool firstIsLeft = a.X <= b.X; |
| 112 | + FrameworkElement leftElement = firstIsLeft ? a : b; |
| 113 | + FrameworkElement rightElement = firstIsLeft ? b : a; |
| 114 | + // вертикальные составляющие прямоугольников - это области между двумя параллельными прямыми, |
| 115 | + // одна из которых образована левой границей фигуры, а другая - правой |
| 116 | + bool verticalIntersects = leftElement.X <= rightElement.X && |
| 117 | + rightElement.X <= leftElement.X + leftElement.Width; |
| 118 | + bool firstIsTop = a.Y <= b.Y; |
| 119 | + FrameworkElement topElement = firstIsTop ? a : b; |
| 120 | + FrameworkElement bottomElement = firstIsTop ? b : a; |
| 121 | + // аналогично определяются горизонтальные составляющие прямоугольников |
| 122 | + bool horizontalIntersects = topElement.Y <= bottomElement.Y && |
| 123 | + bottomElement.Y <= topElement.Y + topElement.Height; |
| 124 | + // если пересекаются и горизонтальные, и вертикальные составляющие - мы имеем |
| 125 | + // пересечение прямоугольников, и можем получить координаты области пересечения |
| 126 | + if (verticalIntersects && horizontalIntersects) { |
| 127 | + // получаем глобальные координаты области пересечения |
| 128 | + int intersectionLeft = rightElement.X; |
| 129 | + int intersectionRight = Math.Min(leftElement.X + leftElement.Width, rightElement.X + rightElement.Width); |
| 130 | + int intersectionTop = bottomElement.Y; |
| 131 | + int intersectionBottom = Math.Min(topElement.Y + topElement.Height, |
| 132 | + bottomElement.Y + bottomElement.Height); |
| 133 | + // возвращаем координаты области пересечения относительно a |
| 134 | + res = new SMALL_RECT((short) (intersectionLeft - a.X), (short) (intersectionTop - a.Y), |
| 135 | + (short) (intersectionRight - a.X), (short) (intersectionBottom - a.Y)); |
| 136 | + } |
| 137 | + return res; |
| 138 | + } |
| 139 | + } |
| 140 | + |
| 141 | + internal class Visibility { |
| 142 | + private List<SMALL_RECT> overlappedRegions; |
| 143 | + public List<SMALL_RECT> OverlappedRegions { |
| 144 | + get { |
| 145 | + return overlappedRegions ?? (overlappedRegions = new List<SMALL_RECT>()); |
| 146 | + } |
| 147 | + } |
| 148 | + |
| 149 | + public FrameworkElementVisibility Type { |
| 150 | + get; |
| 151 | + set; |
| 152 | + } |
| 153 | + |
| 154 | + public Visibility() { |
| 155 | + } |
| 156 | + |
| 157 | + public Visibility(FrameworkElementVisibility type) { |
| 158 | + this.Type = type; |
| 159 | + } |
| 160 | + } |
| 161 | +} |
0 commit comments