mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 04:25:13 +00:00
LibWeb: Move the navigable's cursor position to be owned by the document
Navigables are re-used for navigations within the same tab. Its current ownership of the cursor position is a bit ad-hoc, so nothing in the spec indicates when to reset the cursor, nor do we manually do so. So when a cursor update happens on one page, that cursor is retained on the next page. Instead, let's have the document own the cursor. Each navigation results in a new document, thus we don't need to worry about resetting cursors. This also makes many of the callsites feel nicer. We were previously often going from the node, to the document, to the navigable, to the cursor. This patch removes the navigable hop.
This commit is contained in:
parent
f8d83b2922
commit
faebbbc281
Notes:
github-actions[bot]
2024-08-02 16:41:32 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/faebbbc281d Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/932 Reviewed-by: https://github.com/AtkinsSJ Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/shannonbooth
15 changed files with 155 additions and 159 deletions
|
@ -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> Document::parse_html_unsafe(JS::VM& vm, StringView ht
|
|||
return document;
|
||||
}
|
||||
|
||||
void Document::set_cursor_position(JS::NonnullGCPtr<DOM::Position> 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<EditEventHandler>)
|
||||
{
|
||||
reset_cursor_blink_cycle();
|
||||
|
||||
if (m_cursor_position && is<DOM::Text>(*m_cursor_position->node())) {
|
||||
auto& text_node = static_cast<DOM::Text&>(*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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -688,6 +688,15 @@ public:
|
|||
void set_console_client(JS::GCPtr<JS::ConsoleClient> console_client) { m_console_client = console_client; }
|
||||
JS::GCPtr<JS::ConsoleClient> console_client() const { return m_console_client; }
|
||||
|
||||
JS::GCPtr<DOM::Position> cursor_position() const { return m_cursor_position; }
|
||||
void set_cursor_position(JS::NonnullGCPtr<DOM::Position>);
|
||||
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<EditEventHandler>);
|
||||
|
||||
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<Animations::Animation>);
|
||||
|
||||
void reset_cursor_blink_cycle();
|
||||
|
||||
JS::NonnullGCPtr<Page> m_page;
|
||||
OwnPtr<CSS::StyleComputer> m_style_computer;
|
||||
JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
|
||||
|
@ -950,6 +961,10 @@ private:
|
|||
bool m_allow_declarative_shadow_roots { false };
|
||||
|
||||
JS::GCPtr<JS::ConsoleClient> m_console_client;
|
||||
|
||||
JS::GCPtr<DOM::Position> m_cursor_position;
|
||||
RefPtr<Core::Timer> m_cursor_blink_timer;
|
||||
bool m_cursor_blink_state { false };
|
||||
};
|
||||
|
||||
template<>
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Web::DOM {
|
|||
class EditableTextNodeOwner {
|
||||
public:
|
||||
virtual ~EditableTextNodeOwner() = default;
|
||||
virtual void did_edit_text_node(Badge<HTML::Navigable>) = 0;
|
||||
virtual void did_edit_text_node(Badge<Document>) = 0;
|
||||
};
|
||||
|
||||
class Text
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -407,7 +407,7 @@ WebIDL::ExceptionOr<void> HTMLInputElement::run_input_activation_behavior(DOM::E
|
|||
return {};
|
||||
}
|
||||
|
||||
void HTMLInputElement::did_edit_text_node(Badge<Navigable>)
|
||||
void HTMLInputElement::did_edit_text_node(Badge<DOM::Document>)
|
||||
{
|
||||
// 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<void> 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()
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
WebIDL::ExceptionOr<void> show_picker();
|
||||
|
||||
// ^DOM::EditableTextNodeOwner
|
||||
virtual void did_edit_text_node(Badge<Navigable>) override;
|
||||
virtual void did_edit_text_node(Badge<DOM::Document>) override;
|
||||
|
||||
// ^EventTarget
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-input-element
|
||||
|
|
|
@ -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<Navigable>)
|
||||
void HTMLTextAreaElement::did_edit_text_node(Badge<DOM::Document>)
|
||||
{
|
||||
VERIFY(m_text_node);
|
||||
set_raw_value(m_text_node->data());
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
}
|
||||
|
||||
// ^DOM::EditableTextNodeOwner
|
||||
virtual void did_edit_text_node(Badge<Navigable>) override;
|
||||
virtual void did_edit_text_node(Badge<DOM::Document>) override;
|
||||
|
||||
// ^EventTarget
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#the-tabindex-attribute:the-textarea-element
|
||||
|
|
|
@ -108,21 +108,6 @@ Navigable::Navigable(JS::NonnullGCPtr<Page> 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<EditEventHandler>)
|
||||
{
|
||||
reset_cursor_blink_cycle();
|
||||
|
||||
if (m_cursor_position && is<DOM::Text>(*m_cursor_position->node())) {
|
||||
auto& text_node = static_cast<DOM::Text&>(*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<DOM::Position> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<EditEventHandler>);
|
||||
|
||||
JS::GCPtr<DOM::Position> cursor_position() const { return m_cursor_position; }
|
||||
void set_cursor_position(JS::NonnullGCPtr<DOM::Position>);
|
||||
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<Page>);
|
||||
|
||||
|
@ -256,10 +247,6 @@ private:
|
|||
bool m_needs_repaint { false };
|
||||
|
||||
Web::EventHandler m_event_handler;
|
||||
|
||||
JS::GCPtr<DOM::Position> m_cursor_position;
|
||||
RefPtr<Core::Timer> m_cursor_blink_timer;
|
||||
bool m_cursor_blink_state { false };
|
||||
};
|
||||
|
||||
HashTable<Navigable*>& all_navigables();
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
namespace Web {
|
||||
|
||||
void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtr<DOM::Position> cursor_position)
|
||||
void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtr<DOM::Document> document, JS::NonnullGCPtr<DOM::Position> cursor_position)
|
||||
{
|
||||
auto& node = verify_cast<DOM::Text>(*cursor_position->node());
|
||||
auto& text = node.data();
|
||||
|
@ -33,11 +33,11 @@ void EditEventHandler::handle_delete_character_after(JS::NonnullGCPtr<DOM::Posit
|
|||
builder.append(text.bytes_as_string_view().substring_view(*next_offset));
|
||||
node.set_data(MUST(builder.to_string()));
|
||||
|
||||
m_navigable->did_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<DOM::Document> document, DOM::Range& range)
|
||||
{
|
||||
auto* start = verify_cast<DOM::Text>(range.start_container());
|
||||
auto* end = verify_cast<DOM::Text>(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<DOM::Position> position, u32 code_point)
|
||||
void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Document> document, JS::NonnullGCPtr<DOM::Position> 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<DOM::Position> position, String data)
|
||||
void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Document> document, JS::NonnullGCPtr<DOM::Position> position, String data)
|
||||
{
|
||||
if (is<DOM::Text>(*position->node())) {
|
||||
auto& node = verify_cast<DOM::Text>(*position->node());
|
||||
|
@ -126,6 +126,7 @@ void EditEventHandler::handle_insert(JS::NonnullGCPtr<DOM::Position> position, S
|
|||
position->set_offset(1);
|
||||
}
|
||||
|
||||
m_navigable->did_edit({});
|
||||
document->user_did_edit_document_text({});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,26 +8,23 @@
|
|||
|
||||
#include <AK/Types.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace Web {
|
||||
|
||||
class EditEventHandler {
|
||||
public:
|
||||
explicit EditEventHandler(HTML::Navigable& navigable)
|
||||
: m_navigable(navigable)
|
||||
explicit EditEventHandler()
|
||||
{
|
||||
}
|
||||
|
||||
~EditEventHandler() = default;
|
||||
|
||||
void handle_delete_character_after(JS::NonnullGCPtr<DOM::Position>);
|
||||
void handle_delete(DOM::Range&);
|
||||
void handle_insert(JS::NonnullGCPtr<DOM::Position>, u32 code_point);
|
||||
void handle_insert(JS::NonnullGCPtr<DOM::Position>, String);
|
||||
|
||||
private:
|
||||
JS::NonnullGCPtr<HTML::Navigable> m_navigable;
|
||||
void handle_delete_character_after(JS::NonnullGCPtr<DOM::Document>, JS::NonnullGCPtr<DOM::Position>);
|
||||
void handle_delete(JS::NonnullGCPtr<DOM::Document>, DOM::Range&);
|
||||
void handle_insert(JS::NonnullGCPtr<DOM::Document>, JS::NonnullGCPtr<DOM::Position>, u32 code_point);
|
||||
void handle_insert(JS::NonnullGCPtr<DOM::Document>, JS::NonnullGCPtr<DOM::Position>, String);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ static CSSPixelPoint compute_mouse_event_offset(CSSPixelPoint position, Layout::
|
|||
|
||||
EventHandler::EventHandler(Badge<HTML::Navigable>, HTML::Navigable& navigable)
|
||||
: m_navigable(navigable)
|
||||
, m_edit_event_handler(make<EditEventHandler>(navigable))
|
||||
, m_edit_event_handler(make<EditEventHandler>())
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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<DOM::Text&>(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<DOM::Text&>(*node);
|
||||
if (is<HTML::HTMLInputElement>(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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue