A game framework written with osu! in mind.
at master 95 lines 3.2 kB view raw
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. 2// See the LICENCE file in the repository root for full licence text. 3 4using System.Collections.Generic; 5using System.Linq; 6using osu.Framework.Input.Events; 7using osuTK.Input; 8 9namespace osu.Framework.Graphics.Containers 10{ 11 public class TabbableContainer : TabbableContainer<Drawable> 12 { 13 } 14 15 /// <summary> 16 /// This interface is used for recognizing <see cref="TabbableContainer{T}"/> of any type without reflection. 17 /// </summary> 18 public interface ITabbableContainer 19 { 20 /// <summary> 21 /// Whether this <see cref="ITabbableContainer"/> can be tabbed to. 22 /// </summary> 23 bool CanBeTabbedTo { get; } 24 } 25 26 public class TabbableContainer<T> : Container<T>, ITabbableContainer 27 where T : Drawable 28 { 29 /// <summary> 30 /// Whether this <see cref="TabbableContainer{T}"/> can be tabbed to. 31 /// </summary> 32 public virtual bool CanBeTabbedTo => true; 33 34 /// <summary> 35 /// Allows for tabbing between multiple levels within the TabbableContentContainer. 36 /// </summary> 37 public CompositeDrawable TabbableContentContainer { private get; set; } 38 39 protected override bool OnKeyDown(KeyDownEvent e) 40 { 41 if (TabbableContentContainer == null || e.Key != Key.Tab) 42 return false; 43 44 var nextTab = nextTabStop(TabbableContentContainer, e.ShiftPressed); 45 if (nextTab != null) GetContainingInputManager().ChangeFocus(nextTab); 46 return true; 47 } 48 49 private Drawable nextTabStop(CompositeDrawable target, bool reverse) 50 { 51 Stack<Drawable> stack = new Stack<Drawable>(); 52 stack.Push(target); // Extra push for circular tabbing 53 stack.Push(target); 54 55 bool started = false; 56 57 while (stack.Count > 0) 58 { 59 var drawable = stack.Pop(); 60 61 if (!started) 62 started = ReferenceEquals(drawable, this); 63 else if (drawable is ITabbableContainer tabbable && tabbable.CanBeTabbedTo) 64 return drawable; 65 66 if (drawable is CompositeDrawable composite) 67 { 68 var newChildren = composite.InternalChildren.ToList(); 69 int bound = reverse ? newChildren.Count : 0; 70 71 if (!started) 72 { 73 // Find self, to know starting point 74 int index = newChildren.IndexOf(this); 75 if (index != -1) 76 bound = reverse ? index + 1 : index; 77 } 78 79 if (reverse) 80 { 81 for (int i = 0; i < bound; i++) 82 stack.Push(newChildren[i]); 83 } 84 else 85 { 86 for (int i = newChildren.Count - 1; i >= bound; i--) 87 stack.Push(newChildren[i]); 88 } 89 } 90 } 91 92 return null; 93 } 94 } 95}