Cycle focus to the next (or previous, if `reverse` is true) focusable element. This is called when Tab (or Shift+Tab) is pressed.
(&mut self, reverse: bool)
| 6830 | /// Cycle focus to the next (or previous, if `reverse` is true) focusable element. |
| 6831 | /// This is called when Tab (or Shift+Tab) is pressed. |
| 6832 | pub fn cycle_focus(&mut self, reverse: bool) { |
| 6833 | if self.focusable_elements.is_empty() { |
| 6834 | return; |
| 6835 | } |
| 6836 | self.focus_from_keyboard = true; |
| 6837 | |
| 6838 | // Sort: explicit tab_index first (ascending), then insertion order |
| 6839 | let mut sorted: Vec<FocusableEntry> = self.focusable_elements.clone(); |
| 6840 | sorted.sort_by(|a, b| { |
| 6841 | match (a.tab_index, b.tab_index) { |
| 6842 | (Some(ai), Some(bi)) => ai.cmp(&bi).then(a.insertion_order.cmp(&b.insertion_order)), |
| 6843 | (Some(_), None) => std::cmp::Ordering::Less, |
| 6844 | (None, Some(_)) => std::cmp::Ordering::Greater, |
| 6845 | (None, None) => a.insertion_order.cmp(&b.insertion_order), |
| 6846 | } |
| 6847 | }); |
| 6848 | |
| 6849 | // Find current focus position |
| 6850 | let current_pos = sorted |
| 6851 | .iter() |
| 6852 | .position(|e| e.element_id == self.focused_element_id); |
| 6853 | |
| 6854 | let next_pos = match current_pos { |
| 6855 | Some(pos) => { |
| 6856 | if reverse { |
| 6857 | if pos == 0 { sorted.len() - 1 } else { pos - 1 } |
| 6858 | } else { |
| 6859 | if pos + 1 >= sorted.len() { 0 } else { pos + 1 } |
| 6860 | } |
| 6861 | } |
| 6862 | None => { |
| 6863 | // No current focus — go to first (or last if reverse) |
| 6864 | if reverse { sorted.len() - 1 } else { 0 } |
| 6865 | } |
| 6866 | }; |
| 6867 | |
| 6868 | self.change_focus(sorted[next_pos].element_id); |
| 6869 | } |
| 6870 | |
| 6871 | /// Move focus based on arrow key direction, using `focus_left/right/up/down` overrides. |
| 6872 | pub fn arrow_focus(&mut self, direction: ArrowDirection) { |