From 43dc47a4941d6aaa99ed847f879b4601094f6d4a Mon Sep 17 00:00:00 2001 From: asynts Date: Tue, 1 Dec 2020 23:36:12 +0100 Subject: [PATCH] LibWeb: Add support for range deletion. --- Libraries/LibWeb/DOM/Position.cpp | 18 ++++++++++++ Libraries/LibWeb/DOM/Position.h | 32 ++++++++++++++++++++++ Libraries/LibWeb/Forward.h | 1 + Libraries/LibWeb/Layout/LayoutPosition.cpp | 8 ++++++ Libraries/LibWeb/Layout/LayoutPosition.h | 2 ++ Libraries/LibWeb/Page/EditEventHandler.cpp | 26 ++++++++++++++++-- Libraries/LibWeb/Page/EditEventHandler.h | 1 + Libraries/LibWeb/Page/EventHandler.cpp | 25 ++++++++++++++--- 8 files changed, 107 insertions(+), 6 deletions(-) diff --git a/Libraries/LibWeb/DOM/Position.cpp b/Libraries/LibWeb/DOM/Position.cpp index aa80758f628..90de24663c6 100644 --- a/Libraries/LibWeb/DOM/Position.cpp +++ b/Libraries/LibWeb/DOM/Position.cpp @@ -39,6 +39,24 @@ Position::~Position() { } +Range Range::normalized() const +{ + if (!is_valid()) + return {}; + + if (m_start.node() == m_end.node()) { + if (m_start.offset() <= m_end.offset()) + return *this; + + return { m_end, m_start }; + } + + if (m_start.node()->is_before(*m_end.node())) + return *this; + + return { m_end, m_start }; +} + const LogStream& operator<<(const LogStream& stream, const Position& position) { if (!position.node()) diff --git a/Libraries/LibWeb/DOM/Position.h b/Libraries/LibWeb/DOM/Position.h index 75ef1a2f880..13ad332a1b6 100644 --- a/Libraries/LibWeb/DOM/Position.h +++ b/Libraries/LibWeb/DOM/Position.h @@ -62,6 +62,38 @@ private: unsigned m_offset { 0 }; }; +class Range { +public: + Range() = default; + Range(const Position& start, const Position& end) + : m_start(start) + , m_end(end) + { + } + + bool is_valid() const { return m_start.is_valid() && m_end.is_valid(); } + + void set(const Position& start, const Position& end) + { + m_start = start; + m_end = end; + } + + void set_start(const Position& start) { m_start = start; } + void set_end(const Position& end) { m_end = end; } + + const Position& start() const { return m_start; } + Position& start() { return m_start; } + + const Position& end() const { return m_end; } + Position& end() { return m_end; } + + Range normalized() const; + +private: + Position m_start, m_end; +}; + const LogStream& operator<<(const LogStream&, const Position&); } diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index ec1f5f15690..17674ecba45 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -51,6 +51,7 @@ class MouseEvent; class Node; class ParentNode; class Position; +class Range; class Text; class Timer; class Window; diff --git a/Libraries/LibWeb/Layout/LayoutPosition.cpp b/Libraries/LibWeb/Layout/LayoutPosition.cpp index b2b46b73434..811e9d5067e 100644 --- a/Libraries/LibWeb/Layout/LayoutPosition.cpp +++ b/Libraries/LibWeb/Layout/LayoutPosition.cpp @@ -53,4 +53,12 @@ LayoutRange LayoutRange::normalized() const return { m_end, m_start }; } +DOM::Range LayoutRange::to_dom_range() const +{ + if (!is_valid()) + return {}; + + return { m_start.to_dom_position(), m_end.to_dom_position() }; +} + } diff --git a/Libraries/LibWeb/Layout/LayoutPosition.h b/Libraries/LibWeb/Layout/LayoutPosition.h index 5a7819e81c9..0f19b1208f9 100644 --- a/Libraries/LibWeb/Layout/LayoutPosition.h +++ b/Libraries/LibWeb/Layout/LayoutPosition.h @@ -68,6 +68,8 @@ public: LayoutRange normalized() const; + DOM::Range to_dom_range() const; + private: LayoutPosition m_start; LayoutPosition m_end; diff --git a/Libraries/LibWeb/Page/EditEventHandler.cpp b/Libraries/LibWeb/Page/EditEventHandler.cpp index e8238927886..de397ca2200 100644 --- a/Libraries/LibWeb/Page/EditEventHandler.cpp +++ b/Libraries/LibWeb/Page/EditEventHandler.cpp @@ -30,10 +30,32 @@ #include #include +#include +#include + #include "EditEventHandler.h" namespace Web { +void EditEventHandler::handle_delete(DOM::Range range) +{ + if (range.start().node() != range.end().node()) + TODO(); + + if (is(*range.start().node())) { + auto& node = downcast(*range.start().node()); + + StringBuilder builder; + + builder.append(node.data().substring_view(0, range.start().offset())); + builder.append(node.data().substring_view(range.end().offset())); + node.set_data(builder.to_string()); + + m_frame.document()->layout_node()->set_selection({}); + node.invalidate_style(); + } +} + void EditEventHandler::handle_delete(DOM::Position position) { if (position.offset() == 0) @@ -41,6 +63,7 @@ void EditEventHandler::handle_delete(DOM::Position position) if (is(*position.node())) { auto& node = downcast(*position.node()); + StringBuilder builder; builder.append(node.data().substring_view(0, position.offset() - 1)); builder.append(node.data().substring_view(position.offset())); @@ -53,10 +76,9 @@ void EditEventHandler::handle_delete(DOM::Position position) void EditEventHandler::handle_insert(DOM::Position position, u32 code_point) { - // FIXME: Unicode fiasco. - if (is(*position.node())) { auto& node = downcast(*position.node()); + StringBuilder builder; builder.append(node.data().substring_view(0, position.offset())); builder.append_code_point(code_point); diff --git a/Libraries/LibWeb/Page/EditEventHandler.h b/Libraries/LibWeb/Page/EditEventHandler.h index bb3c2c7a480..3a630d67e39 100644 --- a/Libraries/LibWeb/Page/EditEventHandler.h +++ b/Libraries/LibWeb/Page/EditEventHandler.h @@ -40,6 +40,7 @@ public: virtual ~EditEventHandler() = default; virtual void handle_delete(DOM::Position); + virtual void handle_delete(DOM::Range); virtual void handle_insert(DOM::Position, u32 code_point); private: diff --git a/Libraries/LibWeb/Page/EventHandler.cpp b/Libraries/LibWeb/Page/EventHandler.cpp index 3f50cbbd7f2..4a94a54c774 100644 --- a/Libraries/LibWeb/Page/EventHandler.cpp +++ b/Libraries/LibWeb/Page/EventHandler.cpp @@ -345,16 +345,33 @@ bool EventHandler::handle_keydown(KeyCode key, unsigned modifiers, u32 code_poin return focus_next_element(); } + if (layout_root()->selection().is_valid()) { + auto range = layout_root()->selection().to_dom_range(); + + if (key == KeyCode::Key_Backspace) { + if (range.start().node()->is_editable()) { + m_edit_event_handler->handle_delete(range); + return true; + } + } + + // FIXME: Check if this code point is in the printable character range. + + m_edit_event_handler->handle_delete(range); + m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); + return true; + } + if (m_frame.cursor_position().is_valid() && m_frame.cursor_position().node()->is_editable()) { if (key == KeyCode::Key_Backspace) { m_edit_event_handler->handle_delete(m_frame.cursor_position()); return true; } - if (code_point) { - m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); - return true; - } + // FIXME: Check if this code point is in the printable character range. + + m_edit_event_handler->handle_insert(m_frame.cursor_position(), code_point); + return true; } return false;