mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:52:44 +00:00 
			
		
		
		
	LibGUI: Give GTextDocument access to the GTextEditor during commands
It's useful for the GTextDocument to have access to the initiating GTextEditor widget during the initial execution of a command. Since commands are executed via calls to GUndoCommand::redo(), we do this by wrapping the invocation of redo() in a new helper called GTextDocumentUndoCommand::execute_from(GTextDocument::Client). This is then used to fetch the current auto-indentation feature state and the soft tab width, both used by text insertion.
This commit is contained in:
		
							parent
							
								
									03d73cbaae
								
							
						
					
					
						commit
						14b83ee12f
					
				
					 3 changed files with 57 additions and 43 deletions
				
			
		|  | @ -27,6 +27,7 @@ | ||||||
| #include <AK/StringBuilder.h> | #include <AK/StringBuilder.h> | ||||||
| #include <LibCore/CTimer.h> | #include <LibCore/CTimer.h> | ||||||
| #include <LibGUI/GTextDocument.h> | #include <LibGUI/GTextDocument.h> | ||||||
|  | #include <LibGUI/GTextEditor.h> | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| 
 | 
 | ||||||
| GTextDocument::GTextDocument(Client* client) | GTextDocument::GTextDocument(Client* client) | ||||||
|  | @ -427,7 +428,7 @@ InsertTextCommand::InsertTextCommand(GTextDocument& document, const String& text | ||||||
| 
 | 
 | ||||||
| void InsertTextCommand::redo() | void InsertTextCommand::redo() | ||||||
| { | { | ||||||
|     auto new_cursor = m_document.insert_at(m_range.start(), m_text); |     auto new_cursor = m_document.insert_at(m_range.start(), m_text, m_client); | ||||||
|     // NOTE: We don't know where the range ends until after doing redo().
 |     // NOTE: We don't know where the range ends until after doing redo().
 | ||||||
|     //       This is okay since we always do redo() after adding this to the undo stack.
 |     //       This is okay since we always do redo() after adding this to the undo stack.
 | ||||||
|     m_range.set_end(new_cursor); |     m_range.set_end(new_cursor); | ||||||
|  | @ -464,26 +465,25 @@ void GTextDocument::update_undo_timer() | ||||||
|     m_undo_stack.finalize_current_combo(); |     m_undo_stack.finalize_current_combo(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GTextPosition GTextDocument::insert_at(const GTextPosition& position, const StringView& text) | GTextPosition GTextDocument::insert_at(const GTextPosition& position, const StringView& text, const Client* client) | ||||||
| { | { | ||||||
|     GTextPosition cursor = position; |     GTextPosition cursor = position; | ||||||
|     for (size_t i = 0; i < text.length(); ++i) |     for (size_t i = 0; i < text.length(); ++i) | ||||||
|         cursor = insert_at(cursor, text[i]); |         cursor = insert_at(cursor, text[i], client); | ||||||
|     return cursor; |     return cursor; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| GTextPosition GTextDocument::insert_at(const GTextPosition& position, char ch) | GTextPosition GTextDocument::insert_at(const GTextPosition& position, char ch, const Client* client) | ||||||
| { | { | ||||||
|     // FIXME: We need these from GTextEditor!
 |     bool automatic_indentation_enabled = client ? client->is_automatic_indentation_enabled() : false; | ||||||
|     bool m_automatic_indentation_enabled = true; |     size_t m_soft_tab_width = client ? client->soft_tab_width() : 4; | ||||||
|     size_t m_soft_tab_width = 4; |  | ||||||
| 
 | 
 | ||||||
|     bool at_head = position.column() == 0; |     bool at_head = position.column() == 0; | ||||||
|     bool at_tail = position.column() == line(position.line()).length(); |     bool at_tail = position.column() == line(position.line()).length(); | ||||||
|     if (ch == '\n') { |     if (ch == '\n') { | ||||||
|         if (at_tail || at_head) { |         if (at_tail || at_head) { | ||||||
|             String new_line_contents; |             String new_line_contents; | ||||||
|             if (m_automatic_indentation_enabled && at_tail) { |             if (automatic_indentation_enabled && at_tail) { | ||||||
|                 size_t leading_spaces = 0; |                 size_t leading_spaces = 0; | ||||||
|                 auto& old_line = lines()[position.line()]; |                 auto& old_line = lines()[position.line()]; | ||||||
|                 for (size_t i = 0; i < old_line.length(); ++i) { |                 for (size_t i = 0; i < old_line.length(); ++i) { | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ | ||||||
| class GTextEditor; | class GTextEditor; | ||||||
| class GTextDocument; | class GTextDocument; | ||||||
| class GTextDocumentLine; | class GTextDocumentLine; | ||||||
|  | class GTextDocumentUndoCommand; | ||||||
| 
 | 
 | ||||||
| struct GTextDocumentSpan { | struct GTextDocumentSpan { | ||||||
|     GTextRange range; |     GTextRange range; | ||||||
|  | @ -50,37 +51,6 @@ struct GTextDocumentSpan { | ||||||
|     void* data { nullptr }; |     void* data { nullptr }; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class GTextDocumentUndoCommand : public GCommand { |  | ||||||
| public: |  | ||||||
|     GTextDocumentUndoCommand(GTextDocument&); |  | ||||||
|     virtual ~GTextDocumentUndoCommand(); |  | ||||||
| 
 |  | ||||||
| protected: |  | ||||||
|     GTextDocument& m_document; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class InsertTextCommand : public GTextDocumentUndoCommand { |  | ||||||
| public: |  | ||||||
|     InsertTextCommand(GTextDocument&, const String&, const GTextPosition&); |  | ||||||
|     virtual void undo() override; |  | ||||||
|     virtual void redo() override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     String m_text; |  | ||||||
|     GTextRange m_range; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class RemoveTextCommand : public GTextDocumentUndoCommand { |  | ||||||
| public: |  | ||||||
|     RemoveTextCommand(GTextDocument&, const String&, const GTextRange&); |  | ||||||
|     virtual void undo() override; |  | ||||||
|     virtual void redo() override; |  | ||||||
| 
 |  | ||||||
| private: |  | ||||||
|     String m_text; |  | ||||||
|     GTextRange m_range; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class GTextDocument : public RefCounted<GTextDocument> { | class GTextDocument : public RefCounted<GTextDocument> { | ||||||
| public: | public: | ||||||
|     enum class SearchShouldWrap { |     enum class SearchShouldWrap { | ||||||
|  | @ -98,6 +68,9 @@ public: | ||||||
|         virtual void document_did_change() = 0; |         virtual void document_did_change() = 0; | ||||||
|         virtual void document_did_set_text() = 0; |         virtual void document_did_set_text() = 0; | ||||||
|         virtual void document_did_set_cursor(const GTextPosition&) = 0; |         virtual void document_did_set_cursor(const GTextPosition&) = 0; | ||||||
|  | 
 | ||||||
|  |         virtual bool is_automatic_indentation_enabled() const = 0; | ||||||
|  |         virtual int soft_tab_width() const = 0; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     static NonnullRefPtr<GTextDocument> create(Client* client = nullptr) |     static NonnullRefPtr<GTextDocument> create(Client* client = nullptr) | ||||||
|  | @ -157,8 +130,8 @@ public: | ||||||
|     void notify_did_change(); |     void notify_did_change(); | ||||||
|     void set_all_cursors(const GTextPosition&); |     void set_all_cursors(const GTextPosition&); | ||||||
| 
 | 
 | ||||||
|     GTextPosition insert_at(const GTextPosition&, char); |     GTextPosition insert_at(const GTextPosition&, char, const Client* = nullptr); | ||||||
|     GTextPosition insert_at(const GTextPosition&, const StringView&); |     GTextPosition insert_at(const GTextPosition&, const StringView&, const Client* = nullptr); | ||||||
|     void remove(const GTextRange&); |     void remove(const GTextRange&); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  | @ -201,3 +174,42 @@ private: | ||||||
|     // NOTE: This vector is null terminated.
 |     // NOTE: This vector is null terminated.
 | ||||||
|     Vector<char> m_text; |     Vector<char> m_text; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | class GTextDocumentUndoCommand : public GCommand { | ||||||
|  | public: | ||||||
|  |     GTextDocumentUndoCommand(GTextDocument&); | ||||||
|  |     virtual ~GTextDocumentUndoCommand(); | ||||||
|  | 
 | ||||||
|  |     void execute_from(const GTextDocument::Client& client) | ||||||
|  |     { | ||||||
|  |         m_client = &client; | ||||||
|  |         redo(); | ||||||
|  |         m_client = nullptr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     GTextDocument& m_document; | ||||||
|  |     const GTextDocument::Client* m_client { nullptr }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class InsertTextCommand : public GTextDocumentUndoCommand { | ||||||
|  | public: | ||||||
|  |     InsertTextCommand(GTextDocument&, const String&, const GTextPosition&); | ||||||
|  |     virtual void undo() override; | ||||||
|  |     virtual void redo() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     String m_text; | ||||||
|  |     GTextRange m_range; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class RemoveTextCommand : public GTextDocumentUndoCommand { | ||||||
|  | public: | ||||||
|  |     RemoveTextCommand(GTextDocument&, const String&, const GTextRange&); | ||||||
|  |     virtual void undo() override; | ||||||
|  |     virtual void redo() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     String m_text; | ||||||
|  |     GTextRange m_range; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | @ -60,9 +60,11 @@ public: | ||||||
|     bool is_readonly() const { return m_readonly; } |     bool is_readonly() const { return m_readonly; } | ||||||
|     void set_readonly(bool); |     void set_readonly(bool); | ||||||
| 
 | 
 | ||||||
|     bool is_automatic_indentation_enabled() const { return m_automatic_indentation_enabled; } |     virtual bool is_automatic_indentation_enabled() const final { return m_automatic_indentation_enabled; } | ||||||
|     void set_automatic_indentation_enabled(bool enabled) { m_automatic_indentation_enabled = enabled; } |     void set_automatic_indentation_enabled(bool enabled) { m_automatic_indentation_enabled = enabled; } | ||||||
| 
 | 
 | ||||||
|  |     virtual int soft_tab_width() const final { return m_soft_tab_width; } | ||||||
|  | 
 | ||||||
|     bool is_line_wrapping_enabled() const { return m_line_wrapping_enabled; } |     bool is_line_wrapping_enabled() const { return m_line_wrapping_enabled; } | ||||||
|     void set_line_wrapping_enabled(bool); |     void set_line_wrapping_enabled(bool); | ||||||
| 
 | 
 | ||||||
|  | @ -202,7 +204,7 @@ private: | ||||||
|     inline void execute(Args&&... args) |     inline void execute(Args&&... args) | ||||||
|     { |     { | ||||||
|         auto command = make<T>(*m_document, forward<Args>(args)...); |         auto command = make<T>(*m_document, forward<Args>(args)...); | ||||||
|         command->redo(); |         command->execute_from(*this); | ||||||
|         m_document->add_to_undo_stack(move(command)); |         m_document->add_to_undo_stack(move(command)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling