diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-05-15 02:39:58 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-05-15 02:39:58 +0200 |
commit | ad731cc08f2cf84ec5fca5b1cf303fa0ac3cd390 (patch) | |
tree | 2a8a7e7bcbd269da0d3b16683189652e74a924a7 | |
parent | 01ffcdfa31ba560fcceee82ba2158f867ad114ec (diff) | |
download | serenity-ad731cc08f2cf84ec5fca5b1cf303fa0ac3cd390.zip |
LibGUI: Support cycling through focusable widgets with Tab and Shift-Tab.
-rw-r--r-- | Applications/FontEditor/GlyphEditorWidget.h | 1 | ||||
-rw-r--r-- | Applications/FontEditor/GlyphMapWidget.h | 1 | ||||
-rw-r--r-- | LibGUI/GButton.cpp | 10 | ||||
-rw-r--r-- | LibGUI/GButton.h | 1 | ||||
-rw-r--r-- | LibGUI/GCheckBox.cpp | 10 | ||||
-rw-r--r-- | LibGUI/GTextEditor.cpp | 5 | ||||
-rw-r--r-- | LibGUI/GWidget.cpp | 34 | ||||
-rw-r--r-- | LibGUI/GWidget.h | 2 | ||||
-rw-r--r-- | LibGUI/GWindow.cpp | 24 | ||||
-rw-r--r-- | LibGUI/GWindow.h | 2 |
10 files changed, 82 insertions, 8 deletions
diff --git a/Applications/FontEditor/GlyphEditorWidget.h b/Applications/FontEditor/GlyphEditorWidget.h index 40fdef7e9a..2e9c311ccd 100644 --- a/Applications/FontEditor/GlyphEditorWidget.h +++ b/Applications/FontEditor/GlyphEditorWidget.h @@ -21,7 +21,6 @@ private: virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent&) override; virtual void mousemove_event(GMouseEvent&) override; - virtual bool accepts_focus() const override { return true; } void draw_at_mouse(const GMouseEvent&); diff --git a/Applications/FontEditor/GlyphMapWidget.h b/Applications/FontEditor/GlyphMapWidget.h index eb963e3df7..07aafbe2e1 100644 --- a/Applications/FontEditor/GlyphMapWidget.h +++ b/Applications/FontEditor/GlyphMapWidget.h @@ -27,7 +27,6 @@ public: private: virtual void paint_event(GPaintEvent&) override; virtual void mousedown_event(GMouseEvent&) override; - virtual bool accepts_focus() const override { return true; } Rect get_outer_rect(byte glyph) const; diff --git a/LibGUI/GButton.cpp b/LibGUI/GButton.cpp index 7a957a1f5f..c7e5202872 100644 --- a/LibGUI/GButton.cpp +++ b/LibGUI/GButton.cpp @@ -60,9 +60,15 @@ void GButton::paint_event(GPaintEvent& event) content_rect.move_by(m_icon->width() + 4, 0); content_rect.set_width(content_rect.width() - m_icon->width() - 4); } - if (is_enabled()) + if (is_enabled()) { painter.draw_text(content_rect, m_caption, font, text_alignment(), foreground_color(), TextElision::Right); - else { + if (is_focused()) { + Rect focus_rect = { 0, 0, font.width(m_caption), font.glyph_height() }; + focus_rect.inflate(6, 4); + focus_rect.center_within(content_rect); + painter.draw_rect(focus_rect, Color(140, 140, 140)); + } + } else { painter.draw_text(content_rect.translated(1, 1), m_caption, font, text_alignment(), Color::White, TextElision::Right); painter.draw_text(content_rect, m_caption, font, text_alignment(), Color::from_rgb(0x808080), TextElision::Right); } diff --git a/LibGUI/GButton.h b/LibGUI/GButton.h index 7a163bc537..1175885edb 100644 --- a/LibGUI/GButton.h +++ b/LibGUI/GButton.h @@ -40,6 +40,7 @@ public: void set_action(GAction&); virtual const char* class_name() const override { return "GButton"; } + virtual bool accepts_focus() const override { return true; } protected: virtual void paint_event(GPaintEvent&) override; diff --git a/LibGUI/GCheckBox.cpp b/LibGUI/GCheckBox.cpp index cb14d587f7..913c5ecfa0 100644 --- a/LibGUI/GCheckBox.cpp +++ b/LibGUI/GCheckBox.cpp @@ -60,7 +60,9 @@ void GCheckBox::paint_event(GPaintEvent& event) auto text_rect = rect(); text_rect.set_left(s_box_width + 4); + text_rect.set_width(font().width(m_caption)); text_rect.set_top(height() / 2 - font().glyph_height() / 2); + text_rect.set_height(font().glyph_height()); if (fill_with_background_color()) painter.fill_rect(rect(), background_color()); @@ -78,8 +80,14 @@ void GCheckBox::paint_event(GPaintEvent& event) if (m_checked) painter.draw_bitmap(box_rect.shrunken(4, 4).location(), *s_checked_bitmap, foreground_color()); - if (!caption().is_empty()) + if (!caption().is_empty()) { painter.draw_text(text_rect, caption(), TextAlignment::TopLeft, foreground_color()); + if (is_focused()) { + Rect focus_rect = text_rect; + focus_rect.inflate(6, 4); + painter.draw_rect(focus_rect, Color(140, 140, 140)); + } + } } void GCheckBox::mousemove_event(GMouseEvent& event) diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index 2af1593994..0b11e1ded0 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -315,6 +315,9 @@ void GTextEditor::toggle_selection_if_needed_for_event(const GKeyEvent& event) void GTextEditor::keydown_event(GKeyEvent& event) { + if (is_single_line() && event.key() == KeyCode::Key_Tab) + return GWidget::keydown_event(event); + if (event.key() == KeyCode::Key_Escape) { if (on_escape_pressed) on_escape_pressed(); @@ -505,8 +508,6 @@ void GTextEditor::keydown_event(GKeyEvent& event) if (!is_readonly() && !event.ctrl() && !event.alt() && !event.text().is_empty()) insert_at_cursor_or_replace_selection(event.text()); - - return GWidget::keydown_event(event); } void GTextEditor::delete_current_line() diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 1d98c4d2e1..ba8ce9bccb 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -243,8 +243,14 @@ void GWidget::hide_event(GHideEvent&) { } -void GWidget::keydown_event(GKeyEvent&) +void GWidget::keydown_event(GKeyEvent& event) { + if (!event.alt() && !event.ctrl() && !event.logo() && event.key() == KeyCode::Key_Tab) { + if (event.shift()) + focus_previous_widget(); + else + focus_next_widget(); + } } void GWidget::keyup_event(GKeyEvent&) @@ -543,3 +549,29 @@ void GWidget::set_updates_enabled(bool enabled) if (enabled) update(); } + +void GWidget::focus_previous_widget() +{ + auto focusable_widgets = window()->focusable_widgets(); + for (int i = focusable_widgets.size() - 1; i >= 0; --i) { + if (focusable_widgets[i] != this) + continue; + if (i > 0) + focusable_widgets[i - 1]->set_focus(true); + else + focusable_widgets.last()->set_focus(true); + } +} + +void GWidget::focus_next_widget() +{ + auto focusable_widgets = window()->focusable_widgets(); + for (int i = 0; i < focusable_widgets.size(); ++i) { + if (focusable_widgets[i] != this) + continue; + if (i < focusable_widgets.size() - 1) + focusable_widgets[i + 1]->set_focus(true); + else + focusable_widgets.first()->set_focus(true); + } +} diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index daee5cbf8a..61e896fba1 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -194,6 +194,8 @@ private: void handle_enter_event(CEvent&); void handle_leave_event(CEvent&); void do_layout(); + void focus_previous_widget(); + void focus_next_widget(); CElapsedTimer& click_clock(GMouseButton); diff --git a/LibGUI/GWindow.cpp b/LibGUI/GWindow.cpp index 02c8c84a5c..3f1ca52c07 100644 --- a/LibGUI/GWindow.cpp +++ b/LibGUI/GWindow.cpp @@ -513,3 +513,27 @@ void GWindow::start_wm_resize() message.wm.window_id = m_window_id; GEventLoop::post_message_to_server(message); } + +Vector<GWidget*> GWindow::focusable_widgets() const +{ + if (!m_main_widget) + return { }; + + Vector<GWidget*> collected_widgets; + + Function<void(GWidget&)> collect_focusable_widgets = [&] (GWidget& widget) { + if (widget.accepts_focus()) + collected_widgets.append(&widget); + for (auto& child : widget.children()) { + if (!child->is_widget()) + continue; + auto& child_widget = *static_cast<GWidget*>(child); + if (!child_widget.is_visible()) + continue; + collect_focusable_widgets(child_widget); + } + }; + + collect_focusable_widgets(*m_main_widget); + return collected_widgets; +} diff --git a/LibGUI/GWindow.h b/LibGUI/GWindow.h index 4adf2f7f9d..51c2981618 100644 --- a/LibGUI/GWindow.h +++ b/LibGUI/GWindow.h @@ -113,6 +113,8 @@ public: String icon_path() const { return m_icon_path; } void set_icon_path(const String&); + Vector<GWidget*> focusable_widgets() const; + virtual const char* class_name() const override { return "GWindow"; } protected: |