public IEnumerable <FocusNode> _sortAndFilterHorizontally( TraversalDirection direction, Rect target, FocusNode nearestScope) { D.assert(direction == TraversalDirection.left || direction == TraversalDirection.right); IEnumerable <FocusNode> nodes = nearestScope.traversalDescendants; D.assert(!nodes.Contains(nearestScope)); List <FocusNode> sorted = nodes.ToList(); FocusTravesalUtils.mergeSort <FocusNode>(sorted, compare: (FocusNode a, FocusNode b) => a.rect.center.dx.CompareTo(b.rect.center.dx)); IEnumerable <FocusNode> result = new List <FocusNode>(); switch (direction) { case TraversalDirection.left: result = LinqUtils <FocusNode> .WhereList(sorted, ((FocusNode node) => node.rect != target && node.rect.center.dx <= target.left)); break; case TraversalDirection.right: result = LinqUtils <FocusNode> .WhereList(sorted, ((FocusNode node) => node.rect != target && node.rect.center.dx >= target.right)); break; case TraversalDirection.up: case TraversalDirection.down: break; } return(result); }
public _ReadingOrderSortData _pickNext(List <_ReadingOrderSortData> candidates) { FocusTravesalUtils.mergeSort <_ReadingOrderSortData>(candidates, compare: (_ReadingOrderSortData a, _ReadingOrderSortData b) => a.rect.top.CompareTo(b.rect.top)); _ReadingOrderSortData topmost = candidates.First(); List <_ReadingOrderSortData> inBand(_ReadingOrderSortData current, IEnumerable <_ReadingOrderSortData> _candidates) { Rect band = Rect.fromLTRB(float.NegativeInfinity, current.rect.top, float.PositiveInfinity, current.rect.bottom); return(LinqUtils <_ReadingOrderSortData> .WhereList(_candidates, ((_ReadingOrderSortData item) => { return !item.rect.intersect(band).isEmpty; }))); } List <_ReadingOrderSortData> inBandOfTop = inBand(topmost, candidates); D.assert(topmost.rect.isEmpty || inBandOfTop.isNotEmpty()); if (inBandOfTop.Count <= 1) { return(topmost); } TextDirection nearestCommonDirectionality = _ReadingOrderSortData.commonDirectionalityOf(inBandOfTop); _ReadingOrderSortData.sortWithDirectionality(inBandOfTop, nearestCommonDirectionality); List <_ReadingOrderDirectionalGroupData> bandGroups = _collectDirectionalityGroups(inBandOfTop); if (bandGroups.Count == 1) { return(bandGroups.First().members.First()); } _ReadingOrderDirectionalGroupData.sortWithDirectionality(bandGroups, nearestCommonDirectionality); return(bandGroups.First().members.First()); }
public FocusNode _sortAndFindInitial(FocusNode currentNode, bool vertical = false, bool first = false) { IEnumerable <FocusNode> nodes = currentNode.nearestScope.traversalDescendants; List <FocusNode> sorted = nodes.ToList(); FocusTravesalUtils.mergeSort <FocusNode>(sorted, compare: (FocusNode a, FocusNode b) => { if (vertical) { if (first) { return(a.rect.top.CompareTo(b.rect.top)); } else { return(b.rect.bottom.CompareTo(a.rect.bottom)); } } else { if (first) { return(a.rect.left.CompareTo(b.rect.left)); } else { return(b.rect.right.CompareTo(a.rect.right)); } } }); if (sorted.isNotEmpty()) { return(sorted.First()); } return(null); }
public override IEnumerable <FocusNode> sortDescendants(IEnumerable <FocusNode> descendants) { FocusTraversalPolicy secondaryPolicy = secondary ?? new ReadingOrderTraversalPolicy(); IEnumerable <FocusNode> sortedDescendants = secondaryPolicy.sortDescendants(descendants); List <FocusNode> unordered = new List <FocusNode>(); List <_OrderedFocusInfo> ordered = new List <_OrderedFocusInfo>(); foreach (FocusNode node in sortedDescendants) { FocusOrder order = FocusTraversalOrder.of(node.context, nullOk: true); if (order != null) { ordered.Add(new _OrderedFocusInfo(node: node, order: order)); } else { unordered.Add(node); } } FocusTravesalUtils.mergeSort <_OrderedFocusInfo>(ordered, compare: (_OrderedFocusInfo a, _OrderedFocusInfo b) => { D.assert( a.order.GetType() == b.order.GetType(), () => $"When sorting nodes for determining focus order, the order ({a.order}) of " + $"node {a.node}, isn't the same type as the order ({b.order}) of {b.node}. " + "Incompatible order types can't be compared. Use a FocusTraversalGroup to group " + "similar orders together." ); return(a.order.CompareTo(b.order)); }); return(LinqUtils <FocusNode, _OrderedFocusInfo> .SelectList(ordered, ((_OrderedFocusInfo info) => info.node)).Concat(unordered)); }
public static void sortWithDirectionality(List <_ReadingOrderDirectionalGroupData> list, TextDirection directionality) { FocusTravesalUtils.mergeSort <_ReadingOrderDirectionalGroupData>(list, compare: (_ReadingOrderDirectionalGroupData a, _ReadingOrderDirectionalGroupData b) => { switch (directionality) { case TextDirection.ltr: return(a.rect.left.CompareTo(b.rect.left)); case TextDirection.rtl: return(b.rect.right.CompareTo(a.rect.right)); } D.assert(false, () => "Unhandled directionality $directionality"); return(0); }); }
public IEnumerable <FocusNode> _sortAndFilterVertically( TraversalDirection direction, Rect target, IEnumerable <FocusNode> nodes) { List <FocusNode> sorted = nodes.ToList(); FocusTravesalUtils.mergeSort <FocusNode>(sorted, compare: (FocusNode a, FocusNode b) => a.rect.center.dy.CompareTo(b.rect.center.dy)); switch (direction) { case TraversalDirection.up: return(LinqUtils <FocusNode> .WhereList(sorted, ((FocusNode node) => node.rect != target && node.rect.center.dy <= target.top))); case TraversalDirection.down: return(LinqUtils <FocusNode> .WhereList(sorted, ((FocusNode node) => node.rect != target && node.rect.center.dy >= target.bottom))); case TraversalDirection.left: case TraversalDirection.right: break; } D.assert(direction == TraversalDirection.up || direction == TraversalDirection.down); return(null); }
public static TextDirection commonDirectionalityOf(List <_ReadingOrderSortData> list) { IEnumerable <HashSet <Directionality> > allAncestors = LinqUtils <HashSet <Directionality>, _ReadingOrderSortData> .SelectList(list, ((_ReadingOrderSortData member) => new HashSet <Directionality>(member.directionalAncestors))); HashSet <Directionality> common = null; foreach (HashSet <Directionality> ancestorSet in allAncestors) { common = common ?? ancestorSet; common = FocusTravesalUtils.intersaction(common, ancestorSet); } if (common.isEmpty()) { return(list.First().directionality); } foreach (var com in list.First().directionalAncestors) { if (common.Contains(com)) { return(com.textDirection); } } return(common.First().textDirection); }
public override bool inDirection(FocusNode currentNode, TraversalDirection direction) { FocusScopeNode nearestScope = currentNode.nearestScope; FocusNode focusedChild = nearestScope.focusedChild; if (focusedChild == null) { FocusNode firstFocus = findFirstFocusInDirection(currentNode, direction) ?? currentNode; switch (direction) { case TraversalDirection.up: case TraversalDirection.left: FocusTravesalUtils._focusAndEnsureVisible( firstFocus, alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart ); break; case TraversalDirection.right: case TraversalDirection.down: FocusTravesalUtils._focusAndEnsureVisible( firstFocus, alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd ); break; } return(true); } if (_popPolicyDataIfNeeded(direction, nearestScope, focusedChild)) { return(true); } FocusNode found = null; ScrollableState focusedScrollable = Scrollable.of(focusedChild.context); switch (direction) { case TraversalDirection.down: case TraversalDirection.up: IEnumerable <FocusNode> eligibleNodes = _sortAndFilterVertically( direction, focusedChild.rect, nearestScope.traversalDescendants ); if (focusedScrollable != null && !focusedScrollable.position.atEdge()) { IEnumerable <FocusNode> filteredEligibleNodes = LinqUtils <FocusNode> .WhereList(eligibleNodes, ((FocusNode node) => Scrollable.of(node.context) == focusedScrollable)); if (filteredEligibleNodes.Count() != 0) { eligibleNodes = filteredEligibleNodes; } } if (eligibleNodes.Count() == 0) { break; } List <FocusNode> sorted = eligibleNodes.ToList(); if (direction == TraversalDirection.up) { //sorted = sorted.reversed.toList(); sorted.Reverse(); sorted = sorted.ToList(); } Rect band = Rect.fromLTRB(focusedChild.rect.left, float.NegativeInfinity, focusedChild.rect.right, float.PositiveInfinity); IEnumerable <FocusNode> inBand = LinqUtils <FocusNode> .WhereList(sorted, ((FocusNode node) => !node.rect.intersect(band).isEmpty)); if (inBand.Count() != 0) { found = inBand.First(); break; } FocusTravesalUtils.mergeSort <FocusNode>(sorted, compare: (FocusNode a, FocusNode b) => { return((a.rect.center.dx - focusedChild.rect.center.dx).abs().CompareTo((b.rect.center.dx - focusedChild.rect.center.dx).abs())); }); found = sorted.First(); break; case TraversalDirection.right: case TraversalDirection.left: eligibleNodes = _sortAndFilterHorizontally(direction, focusedChild.rect, nearestScope); if (focusedScrollable != null && !focusedScrollable.position.atEdge()) { IEnumerable <FocusNode> filteredEligibleNodes = LinqUtils <FocusNode> .WhereList(eligibleNodes, ((FocusNode node) => Scrollable.of(node.context) == focusedScrollable)); if (filteredEligibleNodes.Count() != 0) { eligibleNodes = filteredEligibleNodes; } } if (eligibleNodes.Count() == 0) { break; } sorted = eligibleNodes.ToList(); if (direction == TraversalDirection.left) { sorted.Reverse(); sorted = sorted.ToList(); //sorted = sorted.reversed.toList(); } band = Rect.fromLTRB(float.NegativeInfinity, focusedChild.rect.top, float.PositiveInfinity, focusedChild.rect.bottom); inBand = LinqUtils <FocusNode> .WhereList(sorted, ((FocusNode node) => !node.rect.intersect(band).isEmpty)); if (inBand.Count() != 0) { found = inBand.First(); break; } FocusTravesalUtils.mergeSort <FocusNode>(sorted, compare: (FocusNode a, FocusNode b) => { return((a.rect.center.dy - focusedChild.rect.center.dy).abs().CompareTo((b.rect.center.dy - focusedChild.rect.center.dy).abs())); }); found = sorted.First(); break; } if (found != null) { _pushPolicyData(direction, nearestScope, focusedChild); switch (direction) { case TraversalDirection.up: case TraversalDirection.left: FocusTravesalUtils._focusAndEnsureVisible( found, alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart ); break; case TraversalDirection.down: case TraversalDirection.right: FocusTravesalUtils._focusAndEnsureVisible( found, alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd ); break; } return(true); } return(false); }
public bool _popPolicyDataIfNeeded(TraversalDirection direction, FocusScopeNode nearestScope, FocusNode focusedChild) { _DirectionalPolicyData policyData = _policyData[nearestScope]; if (policyData != null && policyData.history.isNotEmpty() && policyData.history.First().direction != direction) { if (policyData.history.Last().node.parent == null) { invalidateScopeData(nearestScope); return(false); } bool popOrInvalidate(TraversalDirection _direction) { FocusNode lastNode = policyData.history.removeLast().node; if (Scrollable.of(lastNode.context) != Scrollable.of(FocusManagerUtils.primaryFocus.context)) { invalidateScopeData(nearestScope); return(false); } ScrollPositionAlignmentPolicy alignmentPolicy = ScrollPositionAlignmentPolicy.explicitPolicy; switch (_direction) { case TraversalDirection.up: case TraversalDirection.left: alignmentPolicy = ScrollPositionAlignmentPolicy.keepVisibleAtStart; break; case TraversalDirection.right: case TraversalDirection.down: alignmentPolicy = ScrollPositionAlignmentPolicy.keepVisibleAtEnd; break; } FocusTravesalUtils._focusAndEnsureVisible( lastNode, alignmentPolicy: alignmentPolicy ); return(true); } switch (direction) { case TraversalDirection.down: case TraversalDirection.up: switch (policyData.history.First().direction) { case TraversalDirection.left: case TraversalDirection.right: invalidateScopeData(nearestScope); break; case TraversalDirection.up: case TraversalDirection.down: if (popOrInvalidate(direction)) { return(true); } break; } break; case TraversalDirection.left: case TraversalDirection.right: switch (policyData.history.First().direction) { case TraversalDirection.left: case TraversalDirection.right: if (popOrInvalidate(direction)) { return(true); } break; case TraversalDirection.up: case TraversalDirection.down: invalidateScopeData(nearestScope); break; } break; } } if (policyData != null && policyData.history.isEmpty()) { invalidateScopeData(nearestScope); } return(false); }
protected bool _moveFocus(FocusNode currentNode, bool forward = false) { D.assert(forward != null); if (currentNode == null) { return(false); } FocusScopeNode nearestScope = currentNode.nearestScope; invalidateScopeData(nearestScope); FocusNode focusedChild = nearestScope.focusedChild; if (focusedChild == null) { FocusNode firstFocus = findFirstFocus(currentNode); if (firstFocus != null) { FocusTravesalUtils._focusAndEnsureVisible( firstFocus, alignmentPolicy: forward ? ScrollPositionAlignmentPolicy.keepVisibleAtEnd : ScrollPositionAlignmentPolicy.keepVisibleAtStart ); return(true); } } List <FocusNode> sortedNodes = _sortAllDescendants(nearestScope); if (forward && focusedChild == sortedNodes.Last()) { FocusTravesalUtils._focusAndEnsureVisible(sortedNodes.First(), alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtEnd); return(true); } if (!forward && focusedChild == sortedNodes.First()) { FocusTravesalUtils._focusAndEnsureVisible(sortedNodes.Last(), alignmentPolicy: ScrollPositionAlignmentPolicy.keepVisibleAtStart); return(true); } IEnumerable <FocusNode> maybeFlipped = new List <FocusNode>(); if (forward) { maybeFlipped = sortedNodes; } else { sortedNodes.Reverse(); maybeFlipped = sortedNodes; } FocusNode previousNode = null; foreach (FocusNode node in maybeFlipped) { if (previousNode == focusedChild) { FocusTravesalUtils._focusAndEnsureVisible( node, alignmentPolicy: forward ? ScrollPositionAlignmentPolicy.keepVisibleAtEnd : ScrollPositionAlignmentPolicy.keepVisibleAtStart ); return(true); } previousNode = node; } return(false); }
public List <FocusNode> _sortAllDescendants(FocusScopeNode scope) { D.assert(scope != null); _FocusTraversalGroupMarker scopeGroupMarker = _getMarker(scope.context); FocusTraversalPolicy defaultPolicy = scopeGroupMarker?.policy ?? new ReadingOrderTraversalPolicy(); Dictionary <FocusNode, _FocusTraversalGroupInfo> groups = new Dictionary <FocusNode, _FocusTraversalGroupInfo>(); foreach (FocusNode node in scope.descendants) { _FocusTraversalGroupMarker groupMarker = _getMarker(node.context); FocusNode groupNode = groupMarker?.focusNode; if (node == groupNode) { BuildContext parentContext = FocusTravesalUtils._getAncestor(groupNode.context, count: 2); _FocusTraversalGroupMarker parentMarker = _getMarker(parentContext); FocusNode parentNode = parentMarker?.focusNode; groups[groupNode] = groups.getOrDefault(parentNode) ?? new _FocusTraversalGroupInfo(parentMarker, members: new List <FocusNode>(), defaultPolicy: defaultPolicy); D.assert(!groups[parentNode].members.Contains(node)); groups[parentNode].members.Add(groupNode); continue; } if (node.canRequestFocus && !node.skipTraversal) { groups[groupNode] = groups.getOrDefault(groupNode) ?? new _FocusTraversalGroupInfo(groupMarker, members: new List <FocusNode>(), defaultPolicy: defaultPolicy); D.assert(!groups[groupNode].members.Contains(node)); groups[groupNode].members.Add(node); } } HashSet <FocusNode> groupKeys = new HashSet <FocusNode>(groups.Keys); foreach (FocusNode key in groups.Keys) { List <FocusNode> sortedMembers = groups.getOrDefault(key).policy.sortDescendants(groups.getOrDefault(key).members).ToList(); groups[key].members.Clear(); groups[key].members.AddRange(sortedMembers); } List <FocusNode> sortedDescendants = new List <FocusNode>(); void visitGroups(_FocusTraversalGroupInfo info) { foreach (FocusNode node in info.members) { if (groupKeys.Contains(node)) { visitGroups(groups[node]); } else { sortedDescendants.Add(node); } } } visitGroups(groups[scopeGroupMarker?.focusNode]); D.assert( FocusTravesalUtils.difference(new HashSet <FocusNode>(sortedDescendants), (new HashSet <FocusNode>(scope.traversalDescendants))).isEmpty(), () => $"sorted descendants contains more nodes than it should: ({FocusTravesalUtils.difference(new HashSet<FocusNode>(sortedDescendants),(new HashSet<FocusNode>(scope.traversalDescendants)))})" ); D.assert( FocusTravesalUtils.difference(new HashSet <FocusNode>(scope.traversalDescendants), new HashSet <FocusNode>(sortedDescendants)).isEmpty(), () => $"sorted descendants are missing some nodes: ({FocusTravesalUtils.difference(new HashSet<FocusNode>(scope.traversalDescendants),new HashSet<FocusNode>(sortedDescendants))})" ); return(sortedDescendants); }
public override void invoke(FocusNode node, Intent intent) => FocusTravesalUtils._focusAndEnsureVisible(node);