1 module rpui.focus_navigator; 2 3 import rpui.widget; 4 import rpui.widget_events; 5 6 package final class FocusNavigator { 7 private Widget holder; 8 9 this (Widget widget) { 10 this.holder = widget; 11 } 12 13 // NOTE: navFocusFront and navFocusBack are symmetrical 14 // focusNext and focusPrev too therefore potential code reduction. 15 package void navFocusFront() { 16 with (holder) { 17 events.notify(FocusFrontEvent()); 18 19 if (skipFocus && isVisible && firstWidget !is null) { 20 firstWidget.focusNavigator.navFocusFront(); 21 } else { 22 if (focusable && isVisible) { 23 focus(); 24 } else { 25 focusNext(); 26 } 27 } 28 } 29 } 30 31 /// Focus to the next widget. 32 void focusNext() { 33 with (holder) { 34 if (skipFocus && isFocused) { 35 navFocusFront(); 36 return; 37 } 38 39 if (parent.lastWidget != holder) { 40 holder.nextWidget.focusNavigator.navFocusFront(); 41 } else { 42 if (parent.finalFocus) { 43 parent.focusNavigator.navFocusFront(); 44 } else { 45 parent.focusNavigator.focusNext(); 46 } 47 } 48 } 49 } 50 51 package void navFocusBack() { 52 with (holder) { 53 events.notify(FocusBackEvent()); 54 55 if (skipFocus && isVisible && lastWidget !is null) { 56 lastWidget.focusNavigator.navFocusBack(); 57 } else { 58 if (focusable && isVisible) { 59 focus(); 60 } else { 61 focusPrev(); 62 } 63 } 64 } 65 } 66 67 /// Focus to the previous widget. 68 void focusPrev() { 69 with (holder) { 70 if (skipFocus && isFocused) { 71 navFocusBack(); 72 return; 73 } 74 75 if (parent.firstWidget != holder) { 76 holder.prevWidget.focusNavigator.navFocusBack(); 77 } else { 78 if (parent.finalFocus) { 79 parent.focusNavigator.navFocusBack(); 80 } else { 81 parent.focusNavigator.focusPrev(); 82 } 83 } 84 } 85 } 86 87 void borderScrollToWidget() { 88 Widget parent = holder.parent; 89 90 while (parent !is null) { 91 auto scrollable = cast(Scrollable) parent; 92 auto focusScrollNavigation = cast(FocusScrollNavigation) parent; 93 parent = parent.parent; 94 95 if (scrollable is null) 96 continue; 97 98 if (focusScrollNavigation is null) { 99 scrollable.scrollToWidget(holder); 100 } else { 101 focusScrollNavigation.borderScrollToWidget(holder); 102 } 103 } 104 } 105 106 /** 107 * Focus primary element in view. 108 */ 109 void focusPrimary() { 110 if (!holder.children.empty) 111 navFocusFront(); 112 } 113 }