diff options
author | Andreas Kling <kling@serenityos.org> | 2020-08-14 19:40:37 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-08-15 00:05:45 +0200 |
commit | 01022eb5d69753d55edcbd538ae8a5a3aabf199c (patch) | |
tree | 1b1a87d5f4f8533b2e5ddfbc6c6a6c3672167512 /Libraries/LibWeb | |
parent | 5939af14d4b8bf8763e094bb6805765319288466 (diff) | |
download | serenity-01022eb5d69753d55edcbd538ae8a5a3aabf199c.zip |
LibWeb: Allow focusing individual (focusable) elements with Tab key
You can now cycle through focusable elements (currently only hyperlinks
are focusable) with the Tab key.
The focus outline is rendered in a new FocusOutline paint phase.
Diffstat (limited to 'Libraries/LibWeb')
-rw-r--r-- | Libraries/LibWeb/DOM/Document.cpp | 14 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/Document.h | 7 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/Element.cpp | 5 | ||||
-rw-r--r-- | Libraries/LibWeb/DOM/Element.h | 3 | ||||
-rw-r--r-- | Libraries/LibWeb/HTML/HTMLAnchorElement.h | 2 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBlock.cpp | 17 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutBox.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutDocument.cpp | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/Layout/LayoutNode.h | 1 | ||||
-rw-r--r-- | Libraries/LibWeb/Page/EventHandler.cpp | 35 | ||||
-rw-r--r-- | Libraries/LibWeb/Page/EventHandler.h | 3 |
11 files changed, 92 insertions, 2 deletions
diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 4796362c5c..488bcde030 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -508,4 +508,18 @@ bool Document::is_editable() const return m_editable; } +void Document::set_focused_element(Element* element) +{ + if (m_focused_element == element) + return; + + if (element) + m_focused_element = element->make_weak_ptr(); + else + m_focused_element = nullptr; + + if (m_layout_root) + m_layout_root->set_needs_display(); +} + } diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index 5e1737c692..fc6910c490 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -161,6 +161,11 @@ public: void set_editable(bool editable) { m_editable = editable; } virtual bool is_editable() const final; + Element* focused_element() { return m_focused_element; } + const Element* focused_element() const { return m_focused_element; } + + void set_focused_element(Element*); + private: virtual RefPtr<LayoutNode> create_layout_node(const CSS::StyleProperties* parent_style) override; @@ -191,6 +196,8 @@ private: QuirksMode m_quirks_mode { QuirksMode::No }; bool m_editable { false }; + + WeakPtr<Element> m_focused_element; }; } diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 0a7f4b473d..19cc1733ad 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -295,4 +295,9 @@ String Element::inner_html() const return builder.to_string(); } +bool Element::is_focused() const +{ + return document().focused_element() == this; +} + } diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 6f8a772994..b08ff34e42 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -87,6 +87,9 @@ public: String inner_html() const; void set_inner_html(StringView); + bool is_focused() const; + virtual bool is_focusable() const { return false; } + protected: RefPtr<LayoutNode> create_layout_node(const CSS::StyleProperties* parent_style) override; diff --git a/Libraries/LibWeb/HTML/HTMLAnchorElement.h b/Libraries/LibWeb/HTML/HTMLAnchorElement.h index 1af777ddec..d2295d0163 100644 --- a/Libraries/LibWeb/HTML/HTMLAnchorElement.h +++ b/Libraries/LibWeb/HTML/HTMLAnchorElement.h @@ -39,6 +39,8 @@ public: String href() const { return attribute(HTML::AttributeNames::href); } String target() const { return attribute(HTML::AttributeNames::target); } + + virtual bool is_focusable() const override { return has_attribute(HTML::AttributeNames::href); } }; } diff --git a/Libraries/LibWeb/Layout/LayoutBlock.cpp b/Libraries/LibWeb/Layout/LayoutBlock.cpp index 59b5011ec6..3a915753bc 100644 --- a/Libraries/LibWeb/Layout/LayoutBlock.cpp +++ b/Libraries/LibWeb/Layout/LayoutBlock.cpp @@ -715,6 +715,23 @@ void LayoutBlock::paint(PaintContext& context, PaintPhase phase) } } } + + if (phase == PaintPhase::FocusOutline) { + if (children_are_inline()) { + for (auto& line_box : m_line_boxes) { + for (auto& fragment : line_box.fragments()) { + auto* node = fragment.layout_node().node(); + if (!node) + continue; + auto* parent = node->parent_element(); + if (!parent) + continue; + if (parent->is_focused()) + context.painter().draw_rect(enclosing_int_rect(fragment.absolute_rect()), context.palette().focus_outline()); + } + } + } + } } HitTestResult LayoutBlock::hit_test(const Gfx::IntPoint& position, HitTestType type) const diff --git a/Libraries/LibWeb/Layout/LayoutBox.cpp b/Libraries/LibWeb/Layout/LayoutBox.cpp index 343173708b..9cfed06ff4 100644 --- a/Libraries/LibWeb/Layout/LayoutBox.cpp +++ b/Libraries/LibWeb/Layout/LayoutBox.cpp @@ -27,9 +27,9 @@ #include <LibGUI/Painter.h> #include <LibWeb/DOM/Document.h> #include <LibWeb/HTML/HTMLBodyElement.h> -#include <LibWeb/Page/Frame.h> #include <LibWeb/Layout/LayoutBlock.h> #include <LibWeb/Layout/LayoutBox.h> +#include <LibWeb/Page/Frame.h> namespace Web { @@ -222,6 +222,10 @@ void LayoutBox::paint(PaintContext& context, PaintPhase phase) context.painter().draw_rect(enclosing_int_rect(padded_rect), Color::Cyan); context.painter().draw_rect(enclosing_int_rect(content_rect), Color::Magenta); } + + if (phase == PaintPhase::FocusOutline && node() && node()->is_element() && downcast<DOM::Element>(*node()).is_focused()) { + context.painter().draw_rect(enclosing_int_rect(absolute_rect()), context.palette().focus_outline()); + } } HitTestResult LayoutBox::hit_test(const Gfx::IntPoint& position, HitTestType type) const diff --git a/Libraries/LibWeb/Layout/LayoutDocument.cpp b/Libraries/LibWeb/Layout/LayoutDocument.cpp index b3255082c5..90232cb7c0 100644 --- a/Libraries/LibWeb/Layout/LayoutDocument.cpp +++ b/Libraries/LibWeb/Layout/LayoutDocument.cpp @@ -105,6 +105,7 @@ void LayoutDocument::paint_all_phases(PaintContext& context) paint(context, PaintPhase::Background); paint(context, PaintPhase::Border); paint(context, PaintPhase::Foreground); + paint(context, PaintPhase::FocusOutline); paint(context, PaintPhase::Overlay); } diff --git a/Libraries/LibWeb/Layout/LayoutNode.h b/Libraries/LibWeb/Layout/LayoutNode.h index 67b8e9d1b9..465376ad9e 100644 --- a/Libraries/LibWeb/Layout/LayoutNode.h +++ b/Libraries/LibWeb/Layout/LayoutNode.h @@ -103,6 +103,7 @@ public: Background, Border, Foreground, + FocusOutline, Overlay, }; virtual void paint(PaintContext&, PaintPhase); diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp index 748ff0f31a..e7281864cc 100644 --- a/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Libraries/LibWeb/Page/EventHandler.cpp @@ -237,8 +237,41 @@ void EventHandler::dump_selection(const char* event_name) const #endif } -bool EventHandler::handle_keydown(KeyCode key, unsigned, u32 code_point) +bool EventHandler::focus_next_element() { + if (!m_frame.document()) + return false; + auto* element = m_frame.document()->focused_element(); + if (!element) { + element = m_frame.document()->first_child_of_type<DOM::Element>(); + if (element && element->is_focusable()) { + m_frame.document()->set_focused_element(element); + return true; + } + } + + for (element = element->next_element_in_pre_order(); element && !element->is_focusable(); element = element->next_element_in_pre_order()) + ; + + m_frame.document()->set_focused_element(element); + return element; +} + +bool EventHandler::focus_previous_element() +{ + // FIXME: Implement Shift-Tab cycling backwards through focusable elements! + return false; +} + +bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_point) +{ + if (key == KeyCode::Key_Tab) { + if (modifiers & KeyModifier::Mod_Shift) + return focus_previous_element(); + else + return focus_next_element(); + } + if (m_frame.cursor_position().node() && m_frame.cursor_position().node()->is_editable()) { // FIXME: Support backspacing across DOM node boundaries. if (key == KeyCode::Key_Backspace && m_frame.cursor_position().offset() > 0) { diff --git a/Libraries/LibWeb/Page/EventHandler.h b/Libraries/LibWeb/Page/EventHandler.h index 6db5a6e2cc..37350184bc 100644 --- a/Libraries/LibWeb/Page/EventHandler.h +++ b/Libraries/LibWeb/Page/EventHandler.h @@ -48,6 +48,9 @@ public: bool handle_keydown(KeyCode, unsigned modifiers, u32 code_point); private: + bool focus_next_element(); + bool focus_previous_element(); + LayoutDocument* layout_root(); const LayoutDocument* layout_root() const; |