From b4af4a4085e206a0c4d3e37778a23b37c3f33cbd Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 2 Jan 2021 01:54:30 +0100 Subject: [PATCH] LibGUI: Allow widget sibling navigation with arrow keys There's no spatial navigation here, Left/Up moves to the previous sibling in the tab order, while Right/Down moves to the next. The arrow keys keep focus within the same parent widget, unlike the tab key which cycles through all focusable widgets in the window. This makes GUI::MessageBox feel a bit nicer since you can now arrow between the Yes/No/Cancel buttons. :^) --- Libraries/LibGUI/Widget.cpp | 34 +++++++++++++++++++++++++--------- Libraries/LibGUI/Widget.h | 4 ++-- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Libraries/LibGUI/Widget.cpp b/Libraries/LibGUI/Widget.cpp index e35fc36536..d7fbd00dd4 100644 --- a/Libraries/LibGUI/Widget.cpp +++ b/Libraries/LibGUI/Widget.cpp @@ -445,13 +445,25 @@ void Widget::hide_event(HideEvent&) void Widget::keydown_event(KeyEvent& event) { - if (!event.alt() && !event.ctrl() && !event.logo() && event.key() == KeyCode::Key_Tab) { - if (event.shift()) - focus_previous_widget(FocusSource::Keyboard); - else - focus_next_widget(FocusSource::Keyboard); - event.accept(); - return; + if (!event.alt() && !event.ctrl() && !event.logo()) { + if (event.key() == KeyCode::Key_Tab) { + if (event.shift()) + focus_previous_widget(FocusSource::Keyboard, false); + else + focus_next_widget(FocusSource::Keyboard, false); + event.accept(); + return; + } + if (!event.shift() && (event.key() == KeyCode::Key_Left || event.key() == KeyCode::Key_Up)) { + focus_previous_widget(FocusSource::Keyboard, true); + event.accept(); + return; + } + if (!event.shift() && (event.key() == KeyCode::Key_Right || event.key() == KeyCode::Key_Down)) { + focus_next_widget(FocusSource::Keyboard, true); + event.accept(); + return; + } } event.ignore(); } @@ -822,9 +834,11 @@ void Widget::set_updates_enabled(bool enabled) update(); } -void Widget::focus_previous_widget(FocusSource source) +void Widget::focus_previous_widget(FocusSource source, bool siblings_only) { auto focusable_widgets = window()->focusable_widgets(source); + if (siblings_only) + focusable_widgets.remove_all_matching([this](auto& entry) { return entry->parent() != parent(); }); for (int i = focusable_widgets.size() - 1; i >= 0; --i) { if (focusable_widgets[i] != this) continue; @@ -835,9 +849,11 @@ void Widget::focus_previous_widget(FocusSource source) } } -void Widget::focus_next_widget(FocusSource source) +void Widget::focus_next_widget(FocusSource source, bool siblings_only) { auto focusable_widgets = window()->focusable_widgets(source); + if (siblings_only) + focusable_widgets.remove_all_matching([this](auto& entry) { return entry->parent() != parent(); }); for (size_t i = 0; i < focusable_widgets.size(); ++i) { if (focusable_widgets[i] != this) continue; diff --git a/Libraries/LibGUI/Widget.h b/Libraries/LibGUI/Widget.h index b130d9a428..5735d7326f 100644 --- a/Libraries/LibGUI/Widget.h +++ b/Libraries/LibGUI/Widget.h @@ -341,8 +341,8 @@ private: void handle_mouseup_event(MouseEvent&); void handle_enter_event(Core::Event&); void handle_leave_event(Core::Event&); - void focus_previous_widget(FocusSource); - void focus_next_widget(FocusSource); + void focus_previous_widget(FocusSource, bool siblings_only); + void focus_next_widget(FocusSource, bool siblings_only); void show_tooltip();