Exemple #1
0
        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);
        }
Exemple #2
0
        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());
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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));
        }
Exemple #5
0
        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);
            });
        }
Exemple #6
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);
        }
Exemple #7
0
        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);
        }
Exemple #8
0
        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);
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        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);
        }
Exemple #12
0
 public override void invoke(FocusNode node, Intent intent) => FocusTravesalUtils._focusAndEnsureVisible(node);