Skip to content

Commit f76e87d

Browse files
committed
Added insert/delete line callbacks and line-based user data (BalazsJako#29)
1 parent 7e0f659 commit f76e87d

4 files changed

Lines changed: 256 additions & 18 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public API to externally implement these features is however included.
7171
- Has marker API to specify lines and/or line numbers to highlight and optional show tooltips (see [example](docs/markers.md)).
7272
- Has optional scrollbar minimap to render cursor, selection and marker locations.
7373
- Provides middle-mouse pan and scroll functions like CAD programs and browsers.
74+
- Has API to attach user data to select or all lines (see [example](docs/userData.md)).
7475
- Has API to decorate each line (useful for debuggers and IDEs) (see [example](docs/lineDecorator.md)).
7576
- Provides optional and customizable right click context menus for line numbers or text lines (see [example](docs/contextMenus.md))
7677
- Provides auto completion for paired glyphs (\[, \{, \(, \", \') (can be turned on and off).

TextEditor.cpp

Lines changed: 116 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222
#include "TextEditor.h"
2323

2424

25+
//
26+
// TextEditor::TextEditor
27+
//
28+
29+
TextEditor::TextEditor() {
30+
SetPalette(defaultPalette);
31+
}
32+
33+
2534
//
2635
// TextEditor::setText
2736
//
@@ -504,10 +513,11 @@ void TextEditor::renderDecorations() {
504513
auto cursorScreenPos = ImGui::GetCursorScreenPos();
505514
auto position = ImVec2(ImGui::GetWindowPos().x + decorationOffset, cursorScreenPos.y + glyphSize.y * firstVisibleLine);
506515
auto widthInPixels = (decoratorWidth < 0.0f) ? -decoratorWidth * glyphSize.x: decoratorWidth;
507-
Decorator decorator{0, widthInPixels, glyphSize.y, glyphSize};
516+
Decorator decorator{0, widthInPixels, glyphSize.y, glyphSize, nullptr};
508517

509518
for (int i = firstVisibleLine; i <= lastVisibleLine; i++) {
510519
decorator.line = i;
520+
decorator.userData = document.getUserData(i);
511521
ImGui::SetCursorScreenPos(position);
512522
ImGui::PushID(i);
513523
decoratorCallback(decorator);
@@ -2938,8 +2948,8 @@ void TextEditor::Cursors::adjustForDelete(iterator start, Coordinate deleteStart
29382948

29392949
void TextEditor::Document::setText(const std::string_view& text) {
29402950
// reset document
2941-
clear();
2942-
emplace_back();
2951+
clearDocument();
2952+
appendLine();
29432953
updated = true;
29442954

29452955
// process UTF-8 and generate lines of glyphs
@@ -2951,7 +2961,7 @@ void TextEditor::Document::setText(const std::string_view& text) {
29512961
i = CodePoint::read(i, end, &character);
29522962

29532963
if (character == '\n') {
2954-
emplace_back();
2964+
appendLine();
29552965

29562966
} else if (insertSpacesOnTabs && character == '\t') {
29572967
auto spaces = ((back().size() / tabSize) + 1) * tabSize - back().size();
@@ -2976,13 +2986,13 @@ void TextEditor::Document::setText(const std::string_view& text) {
29762986

29772987
void TextEditor::Document::setText(const std::vector<std::string_view>& text) {
29782988
// reset document
2979-
clear();
2989+
clearDocument();
29802990
updated = true;
29812991

29822992
if (text.size()) {
29832993
// process input UTF-8 and generate lines of glyphs
29842994
for (auto& line : text) {
2985-
emplace_back();
2995+
appendLine();
29862996
auto i = line.begin();
29872997
auto end = line.end();
29882998

@@ -3004,7 +3014,7 @@ void TextEditor::Document::setText(const std::vector<std::string_view>& text) {
30043014
}
30053015

30063016
} else {
3007-
emplace_back();
3017+
appendLine();
30083018
}
30093019

30103020
// update maximum column counts
@@ -3032,7 +3042,7 @@ TextEditor::Coordinate TextEditor::Document::insertText(Coordinate start, const
30323042

30333043
if (character == '\n') {
30343044
// split line
3035-
insert(line + 1, Line());
3045+
insertLine(lineNo + 1);
30363046
line = begin() + lineNo;
30373047
auto nextLine = begin() + ++lineNo;
30383048

@@ -3089,19 +3099,17 @@ void TextEditor::Document::deleteText(Coordinate start, Coordinate end) {
30893099

30903100
// start and end are on different lines
30913101
} else {
3092-
// remove start of last line
3093-
endLine.erase(endLine.begin(), endLine.begin() + endIndex);
3094-
3095-
// remove full lines
3096-
erase(begin() + start.line + 1, begin() + end.line);
3097-
30983102
// remove end of first line
30993103
startLine.erase(startLine.begin() + startIndex, startLine.end());
31003104

3105+
// remove start of last line
3106+
endLine.erase(endLine.begin(), endLine.begin() + endIndex);
3107+
31013108
// join lines
3102-
auto& nextLine = at(start.line + 1);
3103-
startLine.insert(startLine.end(), nextLine.begin(), nextLine.end());
3104-
erase(begin() + start.line + 1);
3109+
startLine.insert(startLine.end(), endLine.begin(), endLine.end());
3110+
3111+
// delete lines
3112+
deleteLines(start.line + 1, end.line);
31053113
}
31063114

31073115
// mark affected lines for colorization
@@ -3548,6 +3556,41 @@ bool TextEditor::Document::findText(Coordinate from, const std::string_view& tex
35483556
}
35493557

35503558

3559+
//
3560+
// TextEditor::Document::setUserData
3561+
//
3562+
3563+
void TextEditor::Document::setUserData(int line, void* data) {
3564+
if (line >= 0 && line < lineCount()) {
3565+
at(static_cast<size_t>(line)).userData = data;
3566+
}
3567+
}
3568+
3569+
3570+
//
3571+
// TextEditor::Document::getUserData
3572+
//
3573+
3574+
void* TextEditor::Document::getUserData(int line) const {
3575+
if (line >= 0 && line < lineCount()) {
3576+
return at(static_cast<size_t>(line)).userData;
3577+
3578+
} else {
3579+
return nullptr;
3580+
}
3581+
}
3582+
3583+
//
3584+
// TextEditor::Document::iterateUserData
3585+
//
3586+
3587+
void TextEditor::Document::iterateUserData(std::function<void(int line, void* data)> callback) const {
3588+
for (size_t i = 0; i < size(); i++) {
3589+
callback(static_cast<int>(i), at(i).userData);
3590+
}
3591+
}
3592+
3593+
35513594
//
35523595
// TextEditor::Document::isWholeWord
35533596
//
@@ -3721,6 +3764,62 @@ void TextEditor::Document::normalizeCoordinate(float line, float column, Coordin
37213764
}
37223765

37233766

3767+
//
3768+
// TextEditor::Document::appendLine
3769+
//
3770+
3771+
void TextEditor::Document::appendLine() {
3772+
auto& line = emplace_back();
3773+
3774+
if (insertor) {
3775+
line.userData = insertor(static_cast<int>(size() - 1));
3776+
}
3777+
}
3778+
3779+
3780+
//
3781+
// TextEditor::Document::insertLine
3782+
//
3783+
3784+
void TextEditor::Document::insertLine(int offsset) {
3785+
auto line = insert(begin() + offsset, Line());
3786+
3787+
if (insertor) {
3788+
line->userData = insertor(offsset);
3789+
}
3790+
}
3791+
3792+
3793+
//
3794+
// TextEditor::Document::deleteLines
3795+
//
3796+
3797+
void TextEditor::Document::deleteLines(int start, int end) {
3798+
if (deletor) {
3799+
for (auto i = start; i <= end; i++) {
3800+
deletor(i, at(i).userData);
3801+
}
3802+
}
3803+
3804+
erase(begin() + start, begin() + end);
3805+
}
3806+
3807+
3808+
//
3809+
// TextEditor::Document::clearDocument
3810+
//
3811+
3812+
void TextEditor::Document::clearDocument() {
3813+
if (deletor) {
3814+
for (auto i = 0; i <= lineCount(); i++) {
3815+
deletor(i, at(i).userData);
3816+
}
3817+
}
3818+
3819+
clear();
3820+
}
3821+
3822+
37243823
//
37253824
// TextEditor::Transactions::reset
37263825
//

TextEditor.h

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
class TextEditor {
3333
public:
3434
// constructor
35-
TextEditor() { SetPalette(defaultPalette); }
35+
TextEditor();
3636

3737
//
3838
// Below is the public API
@@ -192,12 +192,32 @@ class TextEditor {
192192
inline void ClearMarkers() { clearMarkers(); }
193193
inline bool HasMarkers() const { return markers.size() != 0; }
194194

195+
// line-based callbacks (line numbers are zero-based)
196+
// insertor callbacks are called when lines are added/inserted
197+
// deletor callbacks are called when lines are deleted
198+
// these callbacks work with user data (see below)
199+
// setting either callback to nullptr will deactivate that callback
200+
inline void SetInsertor(std::function<void*(int line)> callback) { document.setInsertor(callback); }
201+
inline void SetDeletor(std::function<void(int line, void* data)> callback) { document.setDeletor(callback); }
202+
203+
// line-based user data (line numbers are zero-based)
204+
// allowing integrators to associate external data with select lines or all lines
205+
// user data is an opaque void* that must be managed externally
206+
// user data is also passed to the decorator callback (see below)
207+
// user data is attached to a line and additions/insertions/deletions don't effect this
208+
// if a line with user data is removed, it won't come back on a redo (yet)
209+
// the deletor callback (if specified) is called when a line is deleted (see above)
210+
inline void SetUserData(int line, void* data) { document.setUserData(line, data); }
211+
inline void* GetUserData(int line) const { return document.getUserData(line); }
212+
inline void IterateUserData(std::function<void(int line, void* data)> callback) const { document.iterateUserData(callback); }
213+
195214
// line-based decoration
196215
struct Decorator {
197216
int line; // zero-based
198217
float width;
199218
float height;
200219
ImVec2 glyphSize;
220+
void* userData;
201221
};
202222

203223
// positive width is number of pixels, negative with is number of glyphs
@@ -667,6 +687,9 @@ class TextEditor {
667687

668688
// do we need to (re)colorize this line
669689
bool colorize = true;
690+
691+
// user data associated with this line
692+
void* userData = nullptr;
670693
};
671694

672695
// the document being edited (Lines of Glyphs)
@@ -729,6 +752,15 @@ class TextEditor {
729752
inline bool isUpdated() { auto result = updated; updated = false; return result; }
730753
inline void resetUpdated() { updated = false; }
731754

755+
// line-based callbacks
756+
inline void setInsertor(std::function<void*(int line )> callback) { insertor = callback; }
757+
inline void setDeletor(std::function<void(int line, void* data)> callback) { deletor = callback; }
758+
759+
// access line user data
760+
void setUserData(int line, void* data);
761+
void* getUserData(int line) const;
762+
void iterateUserData(std::function<void(int line, void* data)> callback) const;
763+
732764
// utility functions
733765
bool isWholeWord(Coordinate start, Coordinate end) const;
734766
inline bool isEndOfLine(Coordinate from) const { return getIndex(from) == at(from.line).size(); }
@@ -743,6 +775,14 @@ class TextEditor {
743775
bool insertSpacesOnTabs = false;
744776
int maxColumn = 0;
745777
bool updated = false;
778+
779+
std::function<void*(int)> insertor;
780+
std::function<void(int, void*)> deletor;
781+
782+
void appendLine();
783+
void insertLine(int line);
784+
void deleteLines(int start, int end);
785+
void clearDocument();
746786
} document;
747787

748788
// single action to be performed on text as part of a larger transaction

0 commit comments

Comments
 (0)