forked from SerenityOS/serenity
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
LibGUI: Move GTextDocument out of GTextEditor
The idea here is to decouple the document from the editor widget so you could have multiple editors being views onto the same document. This doesn't work yet, since the document and editor are coupled in various ways still (including a per-line back-pointer to the editor.)
- Loading branch information
1 parent
1bcbc3f
commit f1c6193
Showing
6 changed files
with
303 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#include <LibGUI/GTextDocument.h> | ||
#include <ctype.h> | ||
|
||
GTextDocument::GTextDocument(GTextEditor& editor) | ||
: m_editor(editor) | ||
{ | ||
m_lines.append(make<GTextDocumentLine>(m_editor)); | ||
} | ||
|
||
void GTextDocument::set_text(const StringView& text) | ||
{ | ||
m_spans.clear(); | ||
m_lines.clear(); | ||
int start_of_current_line = 0; | ||
|
||
auto add_line = [&](int current_position) { | ||
int line_length = current_position - start_of_current_line; | ||
auto line = make<GTextDocumentLine>(m_editor); | ||
if (line_length) | ||
line->set_text(text.substring_view(start_of_current_line, current_position - start_of_current_line)); | ||
m_lines.append(move(line)); | ||
start_of_current_line = current_position + 1; | ||
}; | ||
int i = 0; | ||
for (i = 0; i < text.length(); ++i) { | ||
if (text[i] == '\n') | ||
add_line(i); | ||
} | ||
add_line(i); | ||
} | ||
|
||
int GTextDocumentLine::first_non_whitespace_column() const | ||
{ | ||
for (int i = 0; i < length(); ++i) { | ||
if (!isspace(m_text[i])) | ||
return i; | ||
} | ||
return length(); | ||
} | ||
|
||
GTextDocumentLine::GTextDocumentLine(GTextEditor& editor) | ||
: m_editor(editor) | ||
{ | ||
clear(); | ||
} | ||
|
||
GTextDocumentLine::GTextDocumentLine(GTextEditor& editor, const StringView& text) | ||
: m_editor(editor) | ||
{ | ||
set_text(text); | ||
} | ||
|
||
void GTextDocumentLine::clear() | ||
{ | ||
m_text.clear(); | ||
m_text.append(0); | ||
} | ||
|
||
void GTextDocumentLine::set_text(const StringView& text) | ||
{ | ||
if (text.length() == length() && !memcmp(text.characters_without_null_termination(), characters(), length())) | ||
return; | ||
if (text.is_empty()) { | ||
clear(); | ||
return; | ||
} | ||
m_text.resize(text.length() + 1); | ||
memcpy(m_text.data(), text.characters_without_null_termination(), text.length() + 1); | ||
} | ||
|
||
void GTextDocumentLine::append(const char* characters, int length) | ||
{ | ||
int old_length = m_text.size() - 1; | ||
m_text.resize(m_text.size() + length); | ||
memcpy(m_text.data() + old_length, characters, length); | ||
m_text.last() = 0; | ||
} | ||
|
||
void GTextDocumentLine::append(char ch) | ||
{ | ||
insert(length(), ch); | ||
} | ||
|
||
void GTextDocumentLine::prepend(char ch) | ||
{ | ||
insert(0, ch); | ||
} | ||
|
||
void GTextDocumentLine::insert(int index, char ch) | ||
{ | ||
if (index == length()) { | ||
m_text.last() = ch; | ||
m_text.append(0); | ||
} else { | ||
m_text.insert(index, move(ch)); | ||
} | ||
} | ||
|
||
void GTextDocumentLine::remove(int index) | ||
{ | ||
if (index == length()) { | ||
m_text.take_last(); | ||
m_text.last() = 0; | ||
} else { | ||
m_text.remove(index); | ||
} | ||
} | ||
|
||
void GTextDocumentLine::truncate(int length) | ||
{ | ||
m_text.resize(length + 1); | ||
m_text.last() = 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
#pragma once | ||
|
||
#include <AK/NonnullOwnPtrVector.h> | ||
#include <AK/NonnullRefPtr.h> | ||
#include <AK/RefCounted.h> | ||
#include <LibDraw/Color.h> | ||
#include <LibDraw/Font.h> | ||
#include <LibGUI/GTextRange.h> | ||
|
||
class GTextEditor; | ||
class GTextDocumentLine; | ||
|
||
struct GTextDocumentSpan { | ||
GTextRange range; | ||
Color color; | ||
const Font* font { nullptr }; | ||
}; | ||
|
||
class GTextDocument : public RefCounted<GTextDocument> { | ||
public: | ||
static NonnullRefPtr<GTextDocument> create(GTextEditor& editor) | ||
{ | ||
return adopt(*new GTextDocument(editor)); | ||
} | ||
|
||
int line_count() const { return m_lines.size(); } | ||
const GTextDocumentLine& line(int line_index) const { return m_lines[line_index]; } | ||
GTextDocumentLine& line(int line_index) { return m_lines[line_index]; } | ||
|
||
void set_spans(const Vector<GTextDocumentSpan>& spans) { m_spans = spans; } | ||
|
||
void set_text(const StringView&); | ||
|
||
const NonnullOwnPtrVector<GTextDocumentLine>& lines() const { return m_lines; } | ||
NonnullOwnPtrVector<GTextDocumentLine>& lines() { return m_lines; } | ||
|
||
bool has_spans() const { return !m_spans.is_empty(); } | ||
const Vector<GTextDocumentSpan>& spans() const { return m_spans; } | ||
|
||
private: | ||
explicit GTextDocument(GTextEditor&); | ||
|
||
NonnullOwnPtrVector<GTextDocumentLine> m_lines; | ||
Vector<GTextDocumentSpan> m_spans; | ||
|
||
GTextEditor& m_editor; | ||
}; | ||
|
||
class GTextDocumentLine { | ||
friend class GTextEditor; | ||
friend class GTextDocument; | ||
|
||
public: | ||
explicit GTextDocumentLine(GTextEditor&); | ||
GTextDocumentLine(GTextEditor&, const StringView&); | ||
|
||
StringView view() const { return { characters(), length() }; } | ||
const char* characters() const { return m_text.data(); } | ||
int length() const { return m_text.size() - 1; } | ||
void set_text(const StringView&); | ||
void append(char); | ||
void prepend(char); | ||
void insert(int index, char); | ||
void remove(int index); | ||
void append(const char*, int); | ||
void truncate(int length); | ||
void clear(); | ||
void recompute_visual_lines(); | ||
int visual_line_containing(int column) const; | ||
int first_non_whitespace_column() const; | ||
|
||
template<typename Callback> | ||
void for_each_visual_line(Callback) const; | ||
|
||
private: | ||
GTextEditor& m_editor; | ||
|
||
// NOTE: This vector is null terminated. | ||
Vector<char> m_text; | ||
|
||
Vector<int, 1> m_visual_line_breaks; | ||
Rect m_visual_rect; | ||
}; |
Oops, something went wrong.