private static List <MeasurementResult> FindCandidatesInGroup(DirectionFunction function, Rect focusedBounds, AutomationElement group, System.Windows.Automation.Condition condition) { var results = new List <MeasurementResult>(); var children = group.FindAll(TreeScope.Children, condition)?.Cast <AutomationElement>(); if (children == null) { return(results); } AutomationElement candidate = null; double nearestItemDistance = double.PositiveInfinity; foreach (var element in children) { var info = element.Current; var targetBounds = info.BoundingRectangle; var controlType = info.ControlType; if (controlType == ControlType.TreeItem) { results.AddRange(FindCandidatesInGroup(function, focusedBounds, element, condition)); } if (_ignoreElements.Contains(controlType) || targetBounds == focusedBounds || !function.IsInDirection(targetBounds, focusedBounds) || info.IsOffscreen) { continue; } var distance = function.DistanceCaluculator(targetBounds, focusedBounds, _weightingValue); if (!IsFocusable(info, controlType) || targetBounds == _zero) { distance = double.PositiveInfinity; } if (distance < nearestItemDistance && _enumerateTargets.Contains(controlType)) { nearestItemDistance = distance; candidate = element; } else { results.AddRange(FindCandidatesInGroup(function, focusedBounds, element, condition)); } } if (candidate == null) { return(results); } results.Add(new MeasurementResult(candidate, nearestItemDistance)); return(results); }
private static void InternalMoveTo(Direction direction, Unit unit) { Initialize(); Task.Run(() => SpatialNavigation.UIAssistantAPI.ThemeAPI.SwitchTheme("General")); if (unit == Unit.Group) { _enumerateTargets = _groupTargets; } else { _enumerateTargets = _itemTargets; } var activeWindow = SpatialNavigation.UIAssistantAPI.WindowAPI.ActiveWindow; var rootElement = activeWindow.Element; var current = AutomationElement.FocusedElement; if (current == null || current == rootElement) { MoveToFirst(rootElement); return; } _weightingValue = activeWindow.Bounds.BottomRight.GetDistance(new Point(0, 0)); var currentInfo = current.Current; var parent = current.GetParent(); if (parent != rootElement && unit == Unit.Group) { current = parent; currentInfo = parent.Current; } var focusedBounds = currentInfo.BoundingRectangle; _excludes.Add(focusedBounds); var type = currentInfo.ControlType; var function = new DirectionFunction(direction); if (parent != rootElement && unit != Unit.Group) { if (!TryMoveToSibling(parent, function, focusedBounds)) { TryMove(rootElement, function, focusedBounds, unit); return; } } else { TryMove(rootElement, function, focusedBounds, unit); } }
private static bool TryMoveToSibling(AutomationElement current, DirectionFunction function, Rect focusedBounds) { if (current == null) { return(false); } var condition = new AndCondition(new PropertyCondition(AutomationElement.IsEnabledProperty, true), new PropertyCondition(AutomationElement.IsOffscreenProperty, false)); _excludes.Add(current.Current.BoundingRectangle); var results = FindCandidatesInGroup(function, focusedBounds, current, condition); var destination = results.OrderBy(x => x.Distance).FirstOrDefault()?.Element; if (destination != null) { Move(destination, focusedBounds); return(true); } return(false); }
private static bool TryMove(AutomationElement current, DirectionFunction function, Rect focusedBounds, Unit unit) { if (current == null) { return(false); } var results = FindCandidates(function, focusedBounds, current, unit); var destination = results.OrderBy(x => x.Distance).ElementAtOrDefault(0)?.Element; if (destination != null) { if (destination.Current.ControlType == ControlType.TabItem) { var tab = destination.GetParent(); var tabItems = tab.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TabItem))?.Cast <AutomationElement>(); destination = tabItems?.FirstOrDefault(x => x.IsSelected()) ?? destination; } Move(destination, focusedBounds); return(true); } return(false); }
// Too complex... private static List <MeasurementResult> FindCandidates(DirectionFunction function, Rect focusedBounds, AutomationElement root, Unit unit) { List <MeasurementResult> results = new List <MeasurementResult>(); var children = root.FindAll(TreeScope.Children, System.Windows.Automation.Condition.TrueCondition)?.Cast <AutomationElement>(); if (children == null) { return(results); } AutomationElement candidate = null; List <MeasurementResult> candidates = new List <MeasurementResult>(); List <AutomationElement> focusedAncestors = new List <AutomationElement>(); double nearestFocusableDistance = double.PositiveInfinity; foreach (var element in children) { var info = element.Current; var bounds = info.BoundingRectangle; var controlType = info.ControlType; // for WPF(Tab and TabItem) if (controlType == ControlType.Tab) { focusedAncestors.Add(element); } else if (controlType == ControlType.TabItem) { candidates.Add(new MeasurementResult(element, _weightingValue * 3)); } if (_excludes.Any(x => x == bounds)) { continue; } if (bounds.Contains(focusedBounds) || bounds == _zero) { focusedAncestors.Add(element); continue; } if (!function.IsInDirection(bounds, focusedBounds) || bounds == focusedBounds) { continue; } var distance = function.DistanceCaluculator(bounds, focusedBounds, _weightingValue); if (unit != Unit.Group || !controlType.IsContainer() || controlType == ControlType.Group) { candidates.Add(new MeasurementResult(element, distance)); } if (distance < nearestFocusableDistance) { if (_enumerateTargets.Contains(controlType) && IsFocusable(info, controlType)) { nearestFocusableDistance = distance; candidate = element; } } } focusedAncestors.ForEach(x => results.AddRange(FindCandidates(function, focusedBounds, x, unit))); candidates = candidates.OrderBy(x => x.Distance).ToList(); foreach (var c in candidates) { var condition = new PropertyCondition(AutomationElement.IsEnabledProperty, true); var result = FindCandidatesInGroup(function, focusedBounds, c.Element, condition); if (result.Count > 0) { results.AddRange(result); break; } } if (candidate != null) { results.Add(new MeasurementResult(candidate, nearestFocusableDistance)); } return(results); }