mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 19:15:08 +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