internal void _remove(FocusScopeNode child) { D.assert(child._parent == this); D.assert(child._manager == this._manager); D.assert(this._debugUltimatePreviousSiblingOf(child, equals: this._firstChild)); D.assert(this._debugUltimateNextSiblingOf(child, equals: this._lastChild)); if (child._previousSibling == null) { D.assert(this._firstChild == child); this._firstChild = child._nextSibling; } else { child._previousSibling._nextSibling = child._nextSibling; } if (child._nextSibling == null) { D.assert(this._lastChild == child); this._lastChild = child._previousSibling; } else { child._nextSibling._previousSibling = child._previousSibling; } child._previousSibling = null; child._nextSibling = null; child._parent = null; child._updateManager(null); }
internal void _prepend(FocusScopeNode child) { D.assert(child != this); D.assert(child != this._firstChild); D.assert(child != this._lastChild); D.assert(child._parent == null); D.assert(child._manager == null); D.assert(child._nextSibling == null); D.assert(child._previousSibling == null); D.assert(() => { var node = this; while (node._parent != null) { node = node._parent; } D.assert(node != child); return(true); }); child._parent = this; child._nextSibling = this._firstChild; if (this._firstChild != null) { this._firstChild._previousSibling = child; } this._firstChild = child; this._lastChild = this._lastChild ?? child; child._updateManager(this._manager); }
void _visitChildren(Action <FocusScopeNode> vistor) { FocusScopeNode child = this._firstChild; while (child != null) { vistor.Invoke(child); child = child._nextSibling; } }
bool _debugUltimateNextSiblingOf(FocusScopeNode child, FocusScopeNode equals) { while (child._nextSibling != null) { D.assert(child._nextSibling != child); child = child._nextSibling; } return(child == equals); }
public void unfocus( UnfocusDisposition disposition = UnfocusDisposition.scope ) { D.assert(disposition != null); if (!hasFocus && (_manager == null || _manager._markedForFocus != this)) { return; } FocusScopeNode scope = enclosingScope; if (scope == null) { return; } switch (disposition) { case UnfocusDisposition.scope: if (scope.canRequestFocus) { scope._focusedChildren.Clear(); } while (!scope.canRequestFocus) { scope = scope.enclosingScope ?? _manager?.rootScope; } scope?._doRequestFocus(findFirstFocus: false); break; case UnfocusDisposition.previouslyFocusedChild: if (scope.canRequestFocus) { scope?._focusedChildren?.Remove(this); } while (!scope.canRequestFocus) { scope.enclosingScope?._focusedChildren?.Remove(scope); scope = scope.enclosingScope ?? _manager?.rootScope; } scope?._doRequestFocus(findFirstFocus: true); break; } D.assert(FocusManagerUtils._focusDebug("Unfocused node:", new List <string> { $"primary focus was {this}", $"next focus will be {_manager?._markedForFocus}" })); }
internal FocusNode _findNextFocus() { FocusScopeNode scope = this.rootScope; while (scope._firstChild != null) { scope = scope._firstChild; } return(scope._focus); }
internal void _setFocus(FocusNode node) { D.assert(node != null); D.assert(node._parent == null); D.assert(this._focus == null); this._focus = node; this._focus._parent = this; this._focus._manager = this._manager; this._focus._hasKeyboardToken = true; this._didChangeFocusChain(); }
internal void _resignFocus(FocusNode node) { D.assert(node != null); if (this._focus != node) { return; } this._focus._parent = null; this._focus._manager = null; this._focus = null; this._didChangeFocusChain(); }
public void setFirstFocus(FocusScopeNode child) { D.assert(child != null); D.assert(child._parent == null || child._parent == this); if (this._firstChild == child) { return; } child.detach(); this._prepend(child); D.assert(child._parent == this); this._didChangeFocusChain(); }
public virtual FocusNode findFirstFocus(FocusNode currentNode) { D.assert(currentNode != null); FocusScopeNode scope = currentNode.nearestScope; FocusNode candidate = scope.focusedChild; if (candidate == null && scope.descendants.Any()) { IEnumerable <FocusNode> sorted = _sortAllDescendants(scope); candidate = sorted.Any() ? sorted.First() : null; } candidate = candidate ?? currentNode; return(candidate); }
public override void changedScope(FocusNode node = null, FocusScopeNode oldScope = null) { base.changedScope(node: node, oldScope: oldScope); if (oldScope != null) { var delEntries = LinqUtils <_DirectionalPolicyDataEntry> .WhereList(_policyData[oldScope]?.history, ((_DirectionalPolicyDataEntry entry) => { return(entry.node == node); })); foreach (var delEntry in delEntries) { _policyData[oldScope]?.history?.Remove(delEntry); } } }
internal List <FocusScopeNode> _getFocusPath() { List <FocusScopeNode> nodes = new List <FocusScopeNode> { this }; FocusScopeNode node = this._parent; while (node != null && node != this._manager?.rootScope) { nodes.Add(node); node = node._parent; } return(nodes); }
public void reparentScopeIfNeeded(FocusScopeNode child) { D.assert(child != null); if (child._parent == null || child._parent == this) { return; } if (child.isFirstFocus) { this.setFirstFocus(child); } else { child.detach(); } }
public void _reparent(FocusNode child) { D.assert(child != null); D.assert(child != this, () => "Tried to make a child into a parent of itself."); if (child._parent == this) { D.assert(_children.Contains(child), () => "Found a node that says it's a child, but doesn't appear in the child list."); // The child is already a child of this parent. return; } D.assert(_manager == null || child != _manager.rootScope, () => "Reparenting the root node isn't allowed."); D.assert(!ancestors.Contains(child), () => "The supplied child is already an ancestor of this node. Loops are not allowed."); FocusScopeNode oldScope = child.enclosingScope; bool hadFocus = child.hasFocus; child._parent?._removeChild(child, removeScopeFocus: oldScope != nearestScope); _children.Add(child); child._parent = this; child._ancestors = null; child._updateManager(_manager); foreach (FocusNode ancestor in child.ancestors) { ancestor._descendants = null; } if (hadFocus) { _manager?.primaryFocus?._setAsFocusedChildForScope(); } if (oldScope != null && child.context != null && child.enclosingScope != oldScope) { //UnityEngine.Debug.Log("FocusTraversalGroup.of(child.context, nullOk: true)?.changedScope(node: child, oldScope: oldScope);"); FocusTraversalGroup.of(child.context, nullOk: true)?.changedScope(node: child, oldScope: oldScope); } if (child._requestFocusWhenReparented) { child._doRequestFocus(findFirstFocus: true); child._requestFocusWhenReparented = false; } }
public override Widget build(BuildContext context) { List <Widget> slivers = buildSlivers(context); AxisDirection axisDirection = getDirection(context); ScrollController scrollController = primary ? PrimaryScrollController.of(context) : controller; Scrollable scrollable = new Scrollable( dragStartBehavior: dragStartBehavior, axisDirection: axisDirection, controller: scrollController, physics: physics, viewportBuilder: (viewportContext, offset) => buildViewport(viewportContext, offset, axisDirection, slivers) ); Widget scrollableResult = primary && scrollController != null ? (Widget)PrimaryScrollController.none(child: scrollable) : scrollable; if (keyboardDismissBehavior == ScrollViewKeyboardDismissBehavior.onDrag) { return(new NotificationListener <ScrollUpdateNotification>( child: scrollableResult, onNotification: (ScrollUpdateNotification notification) => { FocusScopeNode focusScope = FocusScope.of(context); if (notification.dragDetails != null && focusScope.hasFocus) { focusScope.unfocus(); } return false; } )); } else { return(scrollableResult); } }
public void _pushPolicyData(TraversalDirection direction, FocusScopeNode nearestScope, FocusNode focusedChild) { _DirectionalPolicyData policyData = _policyData[nearestScope]; if (policyData != null && !(policyData is _DirectionalPolicyData)) { return; } _DirectionalPolicyDataEntry newEntry = new _DirectionalPolicyDataEntry(node: focusedChild, direction: direction); if (policyData != null) { policyData.history.Add(newEntry); } else { _policyData[nearestScope] = new _DirectionalPolicyData(history: new List <_DirectionalPolicyDataEntry>() { newEntry }); } }
public override List <DiagnosticsNode> debugDescribeChildren() { var children = new List <DiagnosticsNode>(); if (this._firstChild != null) { FocusScopeNode child = this._firstChild; int count = 1; while (true) { children.Add(child.toDiagnosticsNode(name: $"child {count}")); if (child == this._lastChild) { break; } child = child._nextSibling; count += 1; } } return(children); }
public FocusScope( Key key = null, FocusScopeNode node = null, Widget child = null, bool autofocus = false, ValueChanged <bool> onFocusChange = null, bool?canRequestFocus = null, bool?skipTraversal = null, FocusOnKeyCallback onKey = null, string debugLabel = null ) : base( key: key, child: child, focusNode: node, autofocus: autofocus, onFocusChange: onFocusChange, canRequestFocus: canRequestFocus, skipTraversal: skipTraversal, onKey: onKey, debugLabel: debugLabel) { D.assert(child != null); }
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); }
public override void invalidateScopeData(FocusScopeNode node) { base.invalidateScopeData(node); _policyData.Remove(node); }
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 virtual void changedScope(FocusNode node = null, FocusScopeNode oldScope = null) { }
public virtual void invalidateScopeData(FocusScopeNode node) { }
public FocusScope(FocusScopeNode node, Widget child, Key key = null, bool autofocus = false) : base(key) { this.node = node; this.child = child; this.autofocus = autofocus; }
public _FocusScopeMarker(FocusScopeNode node, Widget child, Key key = null) : base(key, child) { D.assert(node != null); this.node = node; }