diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index b6555fe520..40f5c733f6 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -371,6 +371,25 @@ Document::Document(JS::Realm& realm, const URL::URL& url, TemporaryDocumentForFr .has_legacy_override_built_ins_interface_extended_attribute = true, }; + m_cursor_blink_timer = Core::Timer::create_repeating(500, [this] { + if (!m_cursor_position) + return; + + auto node = m_cursor_position->node(); + if (!node) + return; + + if (auto navigable = this->navigable(); !navigable || !navigable->is_focused()) + return; + + node->document().update_layout(); + + if (node->paintable()) { + m_cursor_blink_state = !m_cursor_blink_state; + node->paintable()->set_needs_display(); + } + }); + HTML::main_thread_event_loop().register_document({}, *this); } @@ -485,6 +504,7 @@ void Document::visit_edges(Cell::Visitor& visitor) visitor.visit(m_top_layer_elements); visitor.visit(m_top_layer_pending_removals); visitor.visit(m_console_client); + visitor.visit(m_cursor_position); } // https://w3c.github.io/selection-api/#dom-document-getselection @@ -5267,4 +5287,59 @@ JS::NonnullGCPtr Document::parse_html_unsafe(JS::VM& vm, StringView ht return document; } +void Document::set_cursor_position(JS::NonnullGCPtr position) +{ + if (m_cursor_position && m_cursor_position->equals(position)) + return; + + if (m_cursor_position && m_cursor_position->node()->paintable()) + m_cursor_position->node()->paintable()->set_needs_display(); + + m_cursor_position = position; + + if (m_cursor_position && m_cursor_position->node()->paintable()) + m_cursor_position->node()->paintable()->set_needs_display(); + + reset_cursor_blink_cycle(); +} + +bool Document::increment_cursor_position_offset() +{ + if (!m_cursor_position->increment_offset()) + return false; + + reset_cursor_blink_cycle(); + return true; +} + +bool Document::decrement_cursor_position_offset() +{ + if (!m_cursor_position->decrement_offset()) + return false; + + reset_cursor_blink_cycle(); + return true; +} + +void Document::user_did_edit_document_text(Badge) +{ + reset_cursor_blink_cycle(); + + if (m_cursor_position && is(*m_cursor_position->node())) { + auto& text_node = static_cast(*m_cursor_position->node()); + + if (auto* text_node_owner = text_node.editable_text_node_owner()) + text_node_owner->did_edit_text_node({}); + } +} + +void Document::reset_cursor_blink_cycle() +{ + m_cursor_blink_state = true; + m_cursor_blink_timer->restart(); + + if (m_cursor_position && m_cursor_position->node()->paintable()) + m_cursor_position->node()->paintable()->set_needs_display(); +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index 8c5223ef79..edc773a02e 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -688,6 +688,15 @@ public: void set_console_client(JS::GCPtr console_client) { m_console_client = console_client; } JS::GCPtr console_client() const { return m_console_client; } + JS::GCPtr cursor_position() const { return m_cursor_position; } + void set_cursor_position(JS::NonnullGCPtr); + bool increment_cursor_position_offset(); + bool decrement_cursor_position_offset(); + + bool cursor_blink_state() const { return m_cursor_blink_state; } + + void user_did_edit_document_text(Badge); + protected: virtual void initialize(JS::Realm&) override; virtual void visit_edges(Cell::Visitor&) override; @@ -715,6 +724,8 @@ private: void dispatch_events_for_animation_if_necessary(JS::NonnullGCPtr); + void reset_cursor_blink_cycle(); + JS::NonnullGCPtr m_page; OwnPtr m_style_computer; JS::GCPtr m_style_sheets; @@ -950,6 +961,10 @@ private: bool m_allow_declarative_shadow_roots { false }; JS::GCPtr m_console_client; + + JS::GCPtr m_cursor_position; + RefPtr m_cursor_blink_timer; + bool m_cursor_blink_state { false }; }; template<> diff --git a/Userland/Libraries/LibWeb/DOM/Text.h b/Userland/Libraries/LibWeb/DOM/Text.h index 4549f18f57..7c1a9e0ae8 100644 --- a/Userland/Libraries/LibWeb/DOM/Text.h +++ b/Userland/Libraries/LibWeb/DOM/Text.h @@ -15,7 +15,7 @@ namespace Web::DOM { class EditableTextNodeOwner { public: virtual ~EditableTextNodeOwner() = default; - virtual void did_edit_text_node(Badge) = 0; + virtual void did_edit_text_node(Badge) = 0; }; class Text diff --git a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp index d90b520803..7e4bf1f8b0 100644 --- a/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/FormAssociatedElement.cpp @@ -160,10 +160,8 @@ WebIDL::UnsignedLong FormAssociatedElement::selection_start() const // 2. If there is no selection, return the code unit offset within the relevant value to the character that // immediately follows the text entry cursor. - if (auto navigable = form_associated_element_to_html_element().document().navigable()) { - if (auto cursor = navigable->cursor_position()) - return cursor->offset(); - } + if (auto cursor = form_associated_element_to_html_element().document().cursor_position()) + return cursor->offset(); // FIXME: 3. Return the code unit offset within the relevant value to the character that immediately follows the start of // the selection. @@ -191,10 +189,8 @@ WebIDL::UnsignedLong FormAssociatedElement::selection_end() const // 2. If there is no selection, return the code unit offset within the relevant value to the character that // immediately follows the text entry cursor. - if (auto navigable = form_associated_element_to_html_element().document().navigable()) { - if (auto cursor = navigable->cursor_position()) - return cursor->offset(); - } + if (auto cursor = form_associated_element_to_html_element().document().cursor_position()) + return cursor->offset(); // FIXME: 3. Return the code unit offset within the relevant value to the character that immediately follows the end of // the selection. diff --git a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp index 6ebbf87d77..a91cb8251b 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLElement.cpp @@ -713,10 +713,7 @@ void HTMLElement::did_receive_focus() { if (m_content_editable_state != ContentEditableState::True) return; - auto navigable = document().navigable(); - if (!navigable) - return; - navigable->set_cursor_position(DOM::Position::create(realm(), *this, 0)); + document().set_cursor_position(DOM::Position::create(realm(), *this, 0)); } // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp index e0a0c04e0d..94f07734df 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp @@ -407,7 +407,7 @@ WebIDL::ExceptionOr HTMLInputElement::run_input_activation_behavior(DOM::E return {}; } -void HTMLInputElement::did_edit_text_node(Badge) +void HTMLInputElement::did_edit_text_node(Badge) { // An input element's dirty value flag must be set to true whenever the user interacts with the control in a way that changes the value. m_value = value_sanitization_algorithm(m_text_node->data()); @@ -555,8 +555,7 @@ WebIDL::ExceptionOr HTMLInputElement::set_value(String const& value) m_text_node->set_data(m_value); update_placeholder_visibility(); - if (auto navigable = document().navigable()) - navigable->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); + document().set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); } update_shadow_tree(); @@ -1137,11 +1136,8 @@ void HTMLInputElement::did_receive_focus() if (!m_text_node) return; m_text_node->invalidate_style(); - auto navigable = document().navigable(); - if (!navigable) { - return; - } - navigable->set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); + + document().set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); } void HTMLInputElement::did_lose_focus() diff --git a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h index f56cfafed4..190ebcdcaa 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLInputElement.h @@ -143,7 +143,7 @@ public: WebIDL::ExceptionOr show_picker(); // ^DOM::EditableTextNodeOwner - virtual void did_edit_text_node(Badge) override; + virtual void did_edit_text_node(Badge) override; // ^EventTarget // https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-input-element diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp index a6a5e7b595..56ff3bfcd8 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.cpp @@ -71,11 +71,8 @@ void HTMLTextAreaElement::did_receive_focus() if (!m_text_node) return; m_text_node->invalidate_style(); - auto navigable = document().navigable(); - if (!navigable) { - return; - } - navigable->set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); + + document().set_cursor_position(DOM::Position::create(realm(), *m_text_node, 0)); } void HTMLTextAreaElement::did_lose_focus() @@ -165,8 +162,7 @@ void HTMLTextAreaElement::set_value(String const& value) m_text_node->set_data(m_raw_value); update_placeholder_visibility(); - if (auto navigable = document().navigable()) - navigable->set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); + document().set_cursor_position(DOM::Position::create(realm, *m_text_node, m_text_node->data().bytes().size())); } } } @@ -378,7 +374,7 @@ void HTMLTextAreaElement::form_associated_element_attribute_changed(FlyString co } } -void HTMLTextAreaElement::did_edit_text_node(Badge) +void HTMLTextAreaElement::did_edit_text_node(Badge) { VERIFY(m_text_node); set_raw_value(m_text_node->data()); diff --git a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h index c49b458631..0ef2fba9cd 100644 --- a/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h +++ b/Userland/Libraries/LibWeb/HTML/HTMLTextAreaElement.h @@ -37,7 +37,7 @@ public: } // ^DOM::EditableTextNodeOwner - virtual void did_edit_text_node(Badge) override; + virtual void did_edit_text_node(Badge) override; // ^EventTarget // https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-textarea-element diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.cpp b/Userland/Libraries/LibWeb/HTML/Navigable.cpp index 627a362898..6d504b7619 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Userland/Libraries/LibWeb/HTML/Navigable.cpp @@ -108,21 +108,6 @@ Navigable::Navigable(JS::NonnullGCPtr page) , m_event_handler({}, *this) { all_navigables().set(this); - - m_cursor_blink_timer = Core::Timer::create_repeating(500, [this] { - if (!is_focused()) - return; - if (!m_cursor_position) - return; - auto node = m_cursor_position->node(); - if (!node) - return; - node->document().update_layout(); - if (node->paintable()) { - m_cursor_blink_state = !m_cursor_blink_state; - node->paintable()->set_needs_display(); - } - }); } Navigable::~Navigable() @@ -138,7 +123,6 @@ void Navigable::visit_edges(Cell::Visitor& visitor) visitor.visit(m_current_session_history_entry); visitor.visit(m_active_session_history_entry); visitor.visit(m_container); - visitor.visit(m_cursor_position); m_event_handler.visit_edges(visitor); } @@ -2162,46 +2146,11 @@ UserNavigationInvolvement user_navigation_involvement(DOM::Event const& event) return event.is_trusted() ? UserNavigationInvolvement::Activation : UserNavigationInvolvement::None; } -void Navigable::did_edit(Badge) -{ - reset_cursor_blink_cycle(); - - if (m_cursor_position && is(*m_cursor_position->node())) { - auto& text_node = static_cast(*m_cursor_position->node()); - if (auto text_node_owner = text_node.editable_text_node_owner()) - text_node_owner->did_edit_text_node({}); - } -} - -void Navigable::reset_cursor_blink_cycle() -{ - m_cursor_blink_state = true; - m_cursor_blink_timer->restart(); - if (m_cursor_position && m_cursor_position->node()->paintable()) - m_cursor_position->node()->paintable()->set_needs_display(); -} - bool Navigable::is_focused() const { return &m_page->focused_navigable() == this; } -void Navigable::set_cursor_position(JS::NonnullGCPtr position) -{ - if (m_cursor_position && m_cursor_position->equals(position)) - return; - - if (m_cursor_position && m_cursor_position->node()->paintable()) - m_cursor_position->node()->paintable()->set_needs_display(); - - m_cursor_position = position; - - if (m_cursor_position && m_cursor_position->node()->paintable()) - m_cursor_position->node()->paintable()->set_needs_display(); - - reset_cursor_blink_cycle(); -} - static String visible_text_in_range(DOM::Range const& range) { // NOTE: This is an adaption of Range stringification, but we skip over DOM nodes that don't have a corresponding layout node. @@ -2262,20 +2211,4 @@ void Navigable::paste(String const& text) m_event_handler.handle_paste(text); } -bool Navigable::increment_cursor_position_offset() -{ - if (!m_cursor_position->increment_offset()) - return false; - reset_cursor_blink_cycle(); - return true; -} - -bool Navigable::decrement_cursor_position_offset() -{ - if (!m_cursor_position->decrement_offset()) - return false; - reset_cursor_blink_cycle(); - return true; -} - } diff --git a/Userland/Libraries/LibWeb/HTML/Navigable.h b/Userland/Libraries/LibWeb/HTML/Navigable.h index b209b99080..066c3d023b 100644 --- a/Userland/Libraries/LibWeb/HTML/Navigable.h +++ b/Userland/Libraries/LibWeb/HTML/Navigable.h @@ -198,15 +198,6 @@ public: Web::EventHandler& event_handler() { return m_event_handler; } Web::EventHandler const& event_handler() const { return m_event_handler; } - void did_edit(Badge); - - JS::GCPtr cursor_position() const { return m_cursor_position; } - void set_cursor_position(JS::NonnullGCPtr); - bool increment_cursor_position_offset(); - bool decrement_cursor_position_offset(); - - bool cursor_blink_state() const { return m_cursor_blink_state; } - protected: explicit Navigable(JS::NonnullGCPtr); @@ -256,10 +247,6 @@ private: bool m_needs_repaint { false }; Web::EventHandler m_event_handler; - - JS::GCPtr m_cursor_position; - RefPtr m_cursor_blink_timer; - bool m_cursor_blink_state { false }; }; HashTable& all_navigables(); diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp index 0979a83529..a9fdcdbb97 100644 --- a/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.cpp @@ -17,7 +17,7 @@ namespace Web { -void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtr cursor_position) +void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtr document, JS::NonnullGCPtr cursor_position) { auto& node = verify_cast(*cursor_position->node()); auto& text = node.data(); @@ -33,11 +33,11 @@ void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtrdid_edit({}); + document->user_did_edit_document_text({}); } // This method is quite convoluted but this is necessary to make editing feel intuitive. -void EditEventHandler::handle_delete(DOM::Range& range) +void EditEventHandler::handle_delete(JS::NonnullGCPtr document, DOM::Range& range) { auto* start = verify_cast(range.start_container()); auto* end = verify_cast(range.end_container()); @@ -89,17 +89,17 @@ void EditEventHandler::handle_delete(DOM::Range& range) end->remove(); } - m_navigable->did_edit({}); + document->user_did_edit_document_text({}); } -void EditEventHandler::handle_insert(JS::NonnullGCPtr position, u32 code_point) +void EditEventHandler::handle_insert(JS::NonnullGCPtr document, JS::NonnullGCPtr position, u32 code_point) { StringBuilder builder; builder.append_code_point(code_point); - handle_insert(position, MUST(builder.to_string())); + handle_insert(document, position, MUST(builder.to_string())); } -void EditEventHandler::handle_insert(JS::NonnullGCPtr position, String data) +void EditEventHandler::handle_insert(JS::NonnullGCPtr document, JS::NonnullGCPtr position, String data) { if (is(*position->node())) { auto& node = verify_cast(*position->node()); @@ -126,6 +126,7 @@ void EditEventHandler::handle_insert(JS::NonnullGCPtr position, S position->set_offset(1); } - m_navigable->did_edit({}); + document->user_did_edit_document_text({}); } + } diff --git a/Userland/Libraries/LibWeb/Page/EditEventHandler.h b/Userland/Libraries/LibWeb/Page/EditEventHandler.h index 984302a461..6fe91d743f 100644 --- a/Userland/Libraries/LibWeb/Page/EditEventHandler.h +++ b/Userland/Libraries/LibWeb/Page/EditEventHandler.h @@ -8,26 +8,23 @@ #include #include +#include #include namespace Web { class EditEventHandler { public: - explicit EditEventHandler(HTML::Navigable& navigable) - : m_navigable(navigable) + explicit EditEventHandler() { } ~EditEventHandler() = default; - void handle_delete_character_after(JS::NonnullGCPtr); - void handle_delete(DOM::Range&); - void handle_insert(JS::NonnullGCPtr, u32 code_point); - void handle_insert(JS::NonnullGCPtr, String); - -private: - JS::NonnullGCPtr m_navigable; + void handle_delete_character_after(JS::NonnullGCPtr, JS::NonnullGCPtr); + void handle_delete(JS::NonnullGCPtr, DOM::Range&); + void handle_insert(JS::NonnullGCPtr, JS::NonnullGCPtr, u32 code_point); + void handle_insert(JS::NonnullGCPtr, JS::NonnullGCPtr, String); }; } diff --git a/Userland/Libraries/LibWeb/Page/EventHandler.cpp b/Userland/Libraries/LibWeb/Page/EventHandler.cpp index 0700318c57..189b9a0a7a 100644 --- a/Userland/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Userland/Libraries/LibWeb/Page/EventHandler.cpp @@ -139,7 +139,7 @@ static CSSPixelPoint compute_mouse_event_offset(CSSPixelPoint position, Layout:: EventHandler::EventHandler(Badge, HTML::Navigable& navigable) : m_navigable(navigable) - , m_edit_event_handler(make(navigable)) + , m_edit_event_handler(make()) { } @@ -443,7 +443,7 @@ bool EventHandler::handle_mousedown(CSSPixelPoint viewport_position, CSSPixelPoi // FIXME: This is all rather strange. Find a better solution. if (!did_focus_something || paintable->dom_node()->is_editable()) { auto& realm = document->realm(); - m_navigable->set_cursor_position(DOM::Position::create(realm, *paintable->dom_node(), result->index_in_node)); + document->set_cursor_position(DOM::Position::create(realm, *paintable->dom_node(), result->index_in_node)); if (auto selection = document->get_selection()) { auto anchor_node = selection->anchor_node(); if (anchor_node && modifiers & UIEvents::KeyModifier::Mod_Shift) { @@ -569,7 +569,7 @@ bool EventHandler::handle_mousemove(CSSPixelPoint viewport_position, CSSPixelPoi } } if (should_set_cursor_position) - m_navigable->set_cursor_position(DOM::Position::create(realm, *hit->dom_node(), *start_index)); + document.set_cursor_position(DOM::Position::create(realm, *hit->dom_node(), *start_index)); document.navigable()->set_needs_display(); } @@ -603,10 +603,12 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint viewport_position, CSSPixelP if (!m_navigable->active_document()->is_fully_active()) return false; - auto scroll_offset = m_navigable->active_document()->navigable()->viewport_scroll_offset(); + auto& document = *m_navigable->active_document(); + + auto scroll_offset = document.navigable()->viewport_scroll_offset(); auto position = viewport_position.translated(scroll_offset); - m_navigable->active_document()->update_layout(); + document.update_layout(); if (!paint_root()) return false; @@ -685,7 +687,7 @@ bool EventHandler::handle_doubleclick(CSSPixelPoint viewport_position, CSSPixelP }(); auto& realm = node->document().realm(); - m_navigable->set_cursor_position(DOM::Position::create(realm, hit_dom_node, first_word_break_after)); + document.set_cursor_position(DOM::Position::create(realm, hit_dom_node, first_word_break_after)); if (auto selection = node->document().get_selection()) { (void)selection->set_base_and_extent(hit_dom_node, first_word_break_before, hit_dom_node, first_word_break_after); } @@ -806,17 +808,17 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code selection->remove_all_ranges(); // FIXME: This doesn't work for some reason? - m_navigable->set_cursor_position(DOM::Position::create(realm, *range->start_container(), range->start_offset())); + document->set_cursor_position(DOM::Position::create(realm, *range->start_container(), range->start_offset())); if (key == UIEvents::KeyCode::Key_Backspace || key == UIEvents::KeyCode::Key_Delete) { - m_edit_event_handler->handle_delete(*range); + m_edit_event_handler->handle_delete(document, *range); return true; } // FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here. if (!should_ignore_keydown_event(code_point, modifiers)) { - m_edit_event_handler->handle_delete(*range); - m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_navigable->cursor_position() }, code_point); - m_navigable->increment_cursor_position_offset(); + m_edit_event_handler->handle_delete(document, *range); + m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point); + document->increment_cursor_position_offset(); return true; } } @@ -831,53 +833,53 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code if (!continue_) return false; - if (m_navigable->cursor_position() && m_navigable->cursor_position()->node()->is_editable()) { + if (document->cursor_position() && document->cursor_position()->node()->is_editable()) { if (key == UIEvents::KeyCode::Key_Backspace) { - if (!m_navigable->decrement_cursor_position_offset()) { + if (!document->decrement_cursor_position_offset()) { // FIXME: Move to the previous node and delete the last character there. return true; } - m_edit_event_handler->handle_delete_character_after(*m_navigable->cursor_position()); + m_edit_event_handler->handle_delete_character_after(document, *document->cursor_position()); return true; } if (key == UIEvents::KeyCode::Key_Delete) { - if (m_navigable->cursor_position()->offset_is_at_end_of_node()) { + if (document->cursor_position()->offset_is_at_end_of_node()) { // FIXME: Move to the next node and delete the first character there. return true; } - m_edit_event_handler->handle_delete_character_after(*m_navigable->cursor_position()); + m_edit_event_handler->handle_delete_character_after(document, *document->cursor_position()); return true; } if (key == UIEvents::KeyCode::Key_Right) { - if (!m_navigable->increment_cursor_position_offset()) { + if (!document->increment_cursor_position_offset()) { // FIXME: Move to the next node. } return true; } if (key == UIEvents::KeyCode::Key_Left) { - if (!m_navigable->decrement_cursor_position_offset()) { + if (!document->decrement_cursor_position_offset()) { // FIXME: Move to the previous node. } return true; } if (key == UIEvents::KeyCode::Key_Home) { - auto& cursor_position_node = *m_navigable->cursor_position()->node(); + auto& cursor_position_node = *document->cursor_position()->node(); if (cursor_position_node.is_text()) - m_navigable->set_cursor_position(DOM::Position::create(realm, cursor_position_node, 0)); + document->set_cursor_position(DOM::Position::create(realm, cursor_position_node, 0)); return true; } if (key == UIEvents::KeyCode::Key_End) { - auto& cursor_position_node = *m_navigable->cursor_position()->node(); + auto& cursor_position_node = *document->cursor_position()->node(); if (cursor_position_node.is_text()) { auto& text_node = static_cast(cursor_position_node); - m_navigable->set_cursor_position(DOM::Position::create(realm, text_node, (unsigned)text_node.data().bytes().size())); + document->set_cursor_position(DOM::Position::create(realm, text_node, (unsigned)text_node.data().bytes().size())); } return true; } if (key == UIEvents::KeyCode::Key_Return) { HTML::HTMLInputElement* input_element = nullptr; - if (auto node = m_navigable->cursor_position()->node()) { + if (auto node = document->cursor_position()->node()) { if (node->is_text()) { auto& text_node = static_cast(*node); if (is(text_node.editable_text_node_owner())) @@ -898,8 +900,8 @@ bool EventHandler::handle_keydown(UIEvents::KeyCode key, u32 modifiers, u32 code } // FIXME: Text editing shortcut keys (copy/paste etc.) should be handled here. if (!should_ignore_keydown_event(code_point, modifiers)) { - m_edit_event_handler->handle_insert(JS::NonnullGCPtr { *m_navigable->cursor_position() }, code_point); - m_navigable->increment_cursor_position_offset(); + m_edit_event_handler->handle_insert(document, JS::NonnullGCPtr { *document->cursor_position() }, code_point); + document->increment_cursor_position_offset(); return true; } } @@ -960,11 +962,11 @@ void EventHandler::handle_paste(String const& text) if (!active_document->is_fully_active()) return; - if (auto cursor_position = m_navigable->cursor_position()) { + if (auto cursor_position = active_document->cursor_position()) { if (!cursor_position->node()->is_editable()) return; active_document->update_layout(); - m_edit_event_handler->handle_insert(*cursor_position, text); + m_edit_event_handler->handle_insert(*active_document, *cursor_position, text); cursor_position->set_offset(cursor_position->offset() + text.code_points().length()); } } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index f38e828f6d..e0c722f62f 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -559,18 +559,19 @@ void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase ph void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment) { auto const& navigable = *paintable.navigable(); + auto const& document = paintable.document(); if (!navigable.is_focused()) return; - if (!navigable.cursor_blink_state()) + if (!document.cursor_blink_state()) return; - if (navigable.cursor_position()->node() != paintable.dom_node()) + if (document.cursor_position()->node() != paintable.dom_node()) return; // NOTE: This checks if the cursor is before the start or after the end of the fragment. If it is at the end, after all text, it should still be painted. - if (navigable.cursor_position()->offset() < (unsigned)fragment.start() || navigable.cursor_position()->offset() > (unsigned)(fragment.start() + fragment.length())) + if (document.cursor_position()->offset() < (unsigned)fragment.start() || document.cursor_position()->offset() > (unsigned)(fragment.start() + fragment.length())) return; if (!fragment.layout_node().dom_node() || !fragment.layout_node().dom_node()->is_editable()) @@ -580,7 +581,7 @@ void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintabl auto text = fragment.string_view(); CSSPixelRect cursor_rect { - fragment_rect.x() + CSSPixels::nearest_value_for(paintable.layout_node().first_available_font().width(text.substring_view(0, navigable.cursor_position()->offset() - fragment.start()))), + fragment_rect.x() + CSSPixels::nearest_value_for(paintable.layout_node().first_available_font().width(text.substring_view(0, document.cursor_position()->offset() - fragment.start()))), fragment_rect.top(), 1, fragment_rect.height()