mirror of https://github.com/buggins/dlangui.git
editors: modification marks for lines support, part 1
This commit is contained in:
parent
4931e00fec
commit
e7fe0e818c
|
@ -89,6 +89,12 @@ enum TokenCategory : ubyte {
|
||||||
Error_InvalidComment = (15 << TOKEN_CATEGORY_SHIFT) | 4,
|
Error_InvalidComment = (15 << TOKEN_CATEGORY_SHIFT) | 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TextLineMark {
|
||||||
|
}
|
||||||
|
|
||||||
|
class TextLineMarks {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// split dstring by delimiters
|
/// split dstring by delimiters
|
||||||
dstring[] splitDString(dstring source, dchar delimiter = EOL) {
|
dstring[] splitDString(dstring source, dchar delimiter = EOL) {
|
||||||
|
@ -199,6 +205,16 @@ enum EditAction {
|
||||||
SaveContent,
|
SaveContent,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// values for editable line state
|
||||||
|
enum EditStateMark : ubyte {
|
||||||
|
/// content is unchanged - e.g. after loading from file
|
||||||
|
unchanged,
|
||||||
|
/// content is changed and not yet saved
|
||||||
|
changed,
|
||||||
|
/// content is changed, but already saved to file
|
||||||
|
saved,
|
||||||
|
}
|
||||||
|
|
||||||
/// edit operation details for EditableContent
|
/// edit operation details for EditableContent
|
||||||
class EditOperation {
|
class EditOperation {
|
||||||
protected EditAction _action;
|
protected EditAction _action;
|
||||||
|
@ -218,6 +234,11 @@ class EditOperation {
|
||||||
protected dstring[] _content;
|
protected dstring[] _content;
|
||||||
@property ref dstring[] content() { return _content; }
|
@property ref dstring[] content() { return _content; }
|
||||||
|
|
||||||
|
/// line edit marks for old range
|
||||||
|
protected EditStateMark[] _oldEditMarks;
|
||||||
|
@property ref EditStateMark[] oldEditMarks() { return _oldEditMarks; }
|
||||||
|
@property void oldEditMarks(EditStateMark[] marks) { _oldEditMarks = marks; }
|
||||||
|
|
||||||
/// old content for range
|
/// old content for range
|
||||||
protected dstring[] _oldContent;
|
protected dstring[] _oldContent;
|
||||||
@property ref dstring[] oldContent() { return _oldContent; }
|
@property ref dstring[] oldContent() { return _oldContent; }
|
||||||
|
@ -332,6 +353,7 @@ class UndoBuffer {
|
||||||
void saved() {
|
void saved() {
|
||||||
_savedState = _undoList.peekBack;
|
_savedState = _undoList.peekBack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns true if content has been changed since last saved() or clear() call
|
/// returns true if content has been changed since last saved() or clear() call
|
||||||
@property bool modified() {
|
@property bool modified() {
|
||||||
return _savedState !is _undoList.peekBack;
|
return _savedState !is _undoList.peekBack;
|
||||||
|
@ -357,6 +379,7 @@ class EditableContent {
|
||||||
this(bool multiline) {
|
this(bool multiline) {
|
||||||
_multiline = multiline;
|
_multiline = multiline;
|
||||||
_lines.length = 1; // initial state: single empty line
|
_lines.length = 1; // initial state: single empty line
|
||||||
|
_editMarks.length = 1;
|
||||||
_undoBuffer = new UndoBuffer();
|
_undoBuffer = new UndoBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,9 +423,15 @@ class EditableContent {
|
||||||
/// returns true if miltyline content is supported
|
/// returns true if miltyline content is supported
|
||||||
@property bool multiline() { return _multiline; }
|
@property bool multiline() { return _multiline; }
|
||||||
|
|
||||||
|
/// text content by lines
|
||||||
protected dstring[] _lines;
|
protected dstring[] _lines;
|
||||||
|
/// token properties by lines - for syntax highlight
|
||||||
protected TokenPropString[] _tokenProps;
|
protected TokenPropString[] _tokenProps;
|
||||||
|
|
||||||
|
/// line edit marks
|
||||||
|
protected EditStateMark[] _editMarks;
|
||||||
|
@property EditStateMark[] editMarks() { return _editMarks; }
|
||||||
|
|
||||||
/// returns all lines concatenated delimited by '\n'
|
/// returns all lines concatenated delimited by '\n'
|
||||||
@property dstring text() {
|
@property dstring text() {
|
||||||
if (_lines.length == 0)
|
if (_lines.length == 0)
|
||||||
|
@ -437,6 +466,11 @@ class EditableContent {
|
||||||
|
|
||||||
/// call listener to say that content is saved
|
/// call listener to say that content is saved
|
||||||
void notifyContentSaved() {
|
void notifyContentSaved() {
|
||||||
|
// mark all changed lines as saved
|
||||||
|
for (int i = 0; i < _editMarks.length; i++) {
|
||||||
|
if (_editMarks[i] == EditStateMark.changed)
|
||||||
|
_editMarks[i] = EditStateMark.saved;
|
||||||
|
}
|
||||||
TextRange rangeBefore;
|
TextRange rangeBefore;
|
||||||
TextRange rangeAfter;
|
TextRange rangeAfter;
|
||||||
// notify about content change
|
// notify about content change
|
||||||
|
@ -450,6 +484,12 @@ class EditableContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void markChangedLines(int startLine, int endLine) {
|
||||||
|
for (int i = startLine; i < endLine; i++) {
|
||||||
|
_editMarks[i] = EditStateMark.changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// set props arrays size equal to text line sizes, bit fill with unknown token
|
/// set props arrays size equal to text line sizes, bit fill with unknown token
|
||||||
protected void clearTokenProps(int startLine, int endLine) {
|
protected void clearTokenProps(int startLine, int endLine) {
|
||||||
for (int i = startLine; i < endLine; i++) {
|
for (int i = startLine; i < endLine; i++) {
|
||||||
|
@ -464,6 +504,12 @@ class EditableContent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearEditMarks() {
|
||||||
|
_editMarks.length = _lines.length;
|
||||||
|
for (int i = 0; i < _editMarks.length; i++)
|
||||||
|
_editMarks[i] = EditStateMark.unchanged;
|
||||||
|
}
|
||||||
|
|
||||||
/// replace whole text with another content
|
/// replace whole text with another content
|
||||||
@property EditableContent text(dstring newContent) {
|
@property EditableContent text(dstring newContent) {
|
||||||
clearUndo();
|
clearUndo();
|
||||||
|
@ -478,6 +524,7 @@ class EditableContent {
|
||||||
_tokenProps.length = 1;
|
_tokenProps.length = 1;
|
||||||
updateTokenProps(0, cast(int)_lines.length);
|
updateTokenProps(0, cast(int)_lines.length);
|
||||||
}
|
}
|
||||||
|
clearEditMarks();
|
||||||
notifyContentReplaced();
|
notifyContentReplaced();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -500,11 +547,17 @@ class EditableContent {
|
||||||
return index >= 0 && index < _lines.length ? _lines[index] : ""d;
|
return index >= 0 && index < _lines.length ? _lines[index] : ""d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns line token properties one item per character
|
/// returns line token properties one item per character (index is 0 based line number)
|
||||||
TokenPropString lineTokenProps(int index) {
|
TokenPropString lineTokenProps(int index) {
|
||||||
return index >= 0 && index < _tokenProps.length ? _tokenProps[index] : null;
|
return index >= 0 && index < _tokenProps.length ? _tokenProps[index] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns access to line edit mark by line index (0 based)
|
||||||
|
ref EditStateMark editMark(int index) {
|
||||||
|
assert (index >= 0 && index < _editMarks.length);
|
||||||
|
return _editMarks[index];
|
||||||
|
}
|
||||||
|
|
||||||
/// returns text position for end of line lineIndex
|
/// returns text position for end of line lineIndex
|
||||||
TextPosition lineEnd(int lineIndex) {
|
TextPosition lineEnd(int lineIndex) {
|
||||||
return TextPosition(lineIndex, lineLength(lineIndex));
|
return TextPosition(lineIndex, lineLength(lineIndex));
|
||||||
|
@ -550,6 +603,19 @@ class EditableContent {
|
||||||
contentChangeListeners(this, op, rangeBefore, rangeAfter, source);
|
contentChangeListeners(this, op, rangeBefore, rangeAfter, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// return edit marks for specified range
|
||||||
|
EditStateMark[] rangeMarks(TextRange range) {
|
||||||
|
EditStateMark[] res;
|
||||||
|
if (range.empty) {
|
||||||
|
res ~= EditStateMark.unchanged;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
for (int lineIndex = range.start.line; lineIndex <= range.end.line; lineIndex++) {
|
||||||
|
res ~= _editMarks[lineIndex];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// return text for specified range
|
/// return text for specified range
|
||||||
dstring[] rangeText(TextRange range) {
|
dstring[] rangeText(TextRange range) {
|
||||||
dstring[] res;
|
dstring[] res;
|
||||||
|
@ -607,13 +673,16 @@ class EditableContent {
|
||||||
for (int i = start; i < _lines.length - removedCount; i++) {
|
for (int i = start; i < _lines.length - removedCount; i++) {
|
||||||
_lines[i] = _lines[i + removedCount];
|
_lines[i] = _lines[i + removedCount];
|
||||||
_tokenProps[i] = _tokenProps[i + removedCount];
|
_tokenProps[i] = _tokenProps[i + removedCount];
|
||||||
|
_editMarks[i] = _editMarks[i + removedCount];
|
||||||
}
|
}
|
||||||
for (int i = cast(int)_lines.length - removedCount; i < _lines.length; i++) {
|
for (int i = cast(int)_lines.length - removedCount; i < _lines.length; i++) {
|
||||||
_lines[i] = null; // free unused line references
|
_lines[i] = null; // free unused line references
|
||||||
_tokenProps[i] = null; // free unused line references
|
_tokenProps[i] = null; // free unused line references
|
||||||
|
_editMarks[i] = EditStateMark.unchanged; // free unused line references
|
||||||
}
|
}
|
||||||
_lines.length -= removedCount;
|
_lines.length -= removedCount;
|
||||||
_tokenProps.length = _lines.length;
|
_tokenProps.length = _lines.length;
|
||||||
|
_editMarks.length = _lines.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// inserts count empty lines at specified position
|
/// inserts count empty lines at specified position
|
||||||
|
@ -621,13 +690,16 @@ class EditableContent {
|
||||||
assert(count > 0);
|
assert(count > 0);
|
||||||
_lines.length += count;
|
_lines.length += count;
|
||||||
_tokenProps.length = _lines.length;
|
_tokenProps.length = _lines.length;
|
||||||
|
_editMarks.length = _lines.length;
|
||||||
for (int i = cast(int)_lines.length - 1; i >= start + count; i--) {
|
for (int i = cast(int)_lines.length - 1; i >= start + count; i--) {
|
||||||
_lines[i] = _lines[i - count];
|
_lines[i] = _lines[i - count];
|
||||||
_tokenProps[i] = _tokenProps[i - count];
|
_tokenProps[i] = _tokenProps[i - count];
|
||||||
|
_editMarks[i] = _editMarks[i - count];
|
||||||
}
|
}
|
||||||
for (int i = start; i < start + count; i++) {
|
for (int i = start; i < start + count; i++) {
|
||||||
_lines[i] = ""d;
|
_lines[i] = ""d;
|
||||||
_tokenProps[i] = null;
|
_tokenProps[i] = null;
|
||||||
|
_editMarks[i] = EditStateMark.changed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,6 +729,7 @@ class EditableContent {
|
||||||
//Log.d("merging lines ", firstLineHead, " ", newline, " ", lastLineTail);
|
//Log.d("merging lines ", firstLineHead, " ", newline, " ", lastLineTail);
|
||||||
_lines[i] = cast(dstring)buf;
|
_lines[i] = cast(dstring)buf;
|
||||||
clearTokenProps(i, i + 1);
|
clearTokenProps(i, i + 1);
|
||||||
|
markChangedLines(i, i + 1);
|
||||||
//Log.d("merge result: ", _lines[i]);
|
//Log.d("merge result: ", _lines[i]);
|
||||||
} else if (i == after.start.line) {
|
} else if (i == after.start.line) {
|
||||||
dchar[] buf;
|
dchar[] buf;
|
||||||
|
@ -664,15 +737,18 @@ class EditableContent {
|
||||||
buf ~= newline;
|
buf ~= newline;
|
||||||
_lines[i] = cast(dstring)buf;
|
_lines[i] = cast(dstring)buf;
|
||||||
clearTokenProps(i, i + 1);
|
clearTokenProps(i, i + 1);
|
||||||
|
markChangedLines(i, i + 1);
|
||||||
} else if (i == after.end.line) {
|
} else if (i == after.end.line) {
|
||||||
dchar[] buf;
|
dchar[] buf;
|
||||||
buf ~= newline;
|
buf ~= newline;
|
||||||
buf ~= lastLineTail;
|
buf ~= lastLineTail;
|
||||||
_lines[i] = cast(dstring)buf;
|
_lines[i] = cast(dstring)buf;
|
||||||
clearTokenProps(i, i + 1);
|
clearTokenProps(i, i + 1);
|
||||||
|
markChangedLines(i, i + 1);
|
||||||
} else {
|
} else {
|
||||||
_lines[i] = newline; // no dup needed
|
_lines[i] = newline; // no dup needed
|
||||||
clearTokenProps(i, i + 1);
|
clearTokenProps(i, i + 1);
|
||||||
|
markChangedLines(i, i + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -806,6 +882,7 @@ class EditableContent {
|
||||||
assert(rangeBefore.start <= rangeBefore.end);
|
assert(rangeBefore.start <= rangeBefore.end);
|
||||||
//correctRange(rangeBefore);
|
//correctRange(rangeBefore);
|
||||||
dstring[] oldcontent = rangeText(rangeBefore);
|
dstring[] oldcontent = rangeText(rangeBefore);
|
||||||
|
EditStateMark[] oldmarks = rangeMarks(rangeBefore);
|
||||||
dstring[] newcontent = op.content;
|
dstring[] newcontent = op.content;
|
||||||
if (newcontent.length == 0)
|
if (newcontent.length == 0)
|
||||||
newcontent ~= ""d;
|
newcontent ~= ""d;
|
||||||
|
@ -822,6 +899,7 @@ class EditableContent {
|
||||||
assert(rangeAfter.start <= rangeAfter.end);
|
assert(rangeAfter.start <= rangeAfter.end);
|
||||||
op.newRange = rangeAfter;
|
op.newRange = rangeAfter;
|
||||||
op.oldContent = oldcontent;
|
op.oldContent = oldcontent;
|
||||||
|
op.oldEditMarks = oldmarks;
|
||||||
replaceRange(rangeBefore, rangeAfter, newcontent);
|
replaceRange(rangeBefore, rangeAfter, newcontent);
|
||||||
_undoBuffer.saveForUndo(op);
|
_undoBuffer.saveForUndo(op);
|
||||||
handleContentChange(op, rangeBefore, rangeAfter, source);
|
handleContentChange(op, rangeBefore, rangeAfter, source);
|
||||||
|
|
|
@ -242,6 +242,16 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void drawLeftPaneModificationMarks(DrawBuf buf, Rect rc, int line) {
|
protected void drawLeftPaneModificationMarks(DrawBuf buf, Rect rc, int line) {
|
||||||
|
if (line >= 0 && line < content.length) {
|
||||||
|
EditStateMark m = content.editMark(line);
|
||||||
|
if (m == EditStateMark.changed) {
|
||||||
|
// modified, not saved
|
||||||
|
buf.fillRect(rc, 0xFFD040);
|
||||||
|
} else if (m == EditStateMark.saved) {
|
||||||
|
// modified, not saved
|
||||||
|
buf.fillRect(rc, 0x80FF60);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void drawLeftPaneLineNumbers(DrawBuf buf, Rect rc, int line) {
|
protected void drawLeftPaneLineNumbers(DrawBuf buf, Rect rc, int line) {
|
||||||
|
@ -443,6 +453,51 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// when true, show icons like bookmarks or breakpoints at the left
|
||||||
|
@property bool showIcons() {
|
||||||
|
return _showIcons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// when true, show icons like bookmarks or breakpoints at the left
|
||||||
|
@property EditWidgetBase showIcons(bool flg) {
|
||||||
|
if (_showIcons != flg) {
|
||||||
|
_showIcons = flg;
|
||||||
|
updateLeftPaneWidth();
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// when true, show folding controls at the left
|
||||||
|
@property bool showFolding() {
|
||||||
|
return _showFolding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// when true, show folding controls at the left
|
||||||
|
@property EditWidgetBase showFolding(bool flg) {
|
||||||
|
if (_showFolding != flg) {
|
||||||
|
_showFolding = flg;
|
||||||
|
updateLeftPaneWidth();
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// when true, show modification marks for lines (whether line is unchanged/modified/modified_saved
|
||||||
|
@property bool showModificationMarks() {
|
||||||
|
return _showModificationMarks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// when true, show modification marks for lines (whether line is unchanged/modified/modified_saved
|
||||||
|
@property EditWidgetBase showModificationMarks(bool flg) {
|
||||||
|
if (_showModificationMarks != flg) {
|
||||||
|
_showModificationMarks = flg;
|
||||||
|
updateLeftPaneWidth();
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// when true, line numbers are shown
|
/// when true, line numbers are shown
|
||||||
@property bool showLineNumbers() {
|
@property bool showLineNumbers() {
|
||||||
return _showLineNumbers;
|
return _showLineNumbers;
|
||||||
|
|
|
@ -31,7 +31,7 @@ class SourceEdit : EditBox {
|
||||||
fontSize = 14;
|
fontSize = 14;
|
||||||
layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
|
||||||
minFontSize(10).maxFontSize(75); // allow font zoom with Ctrl + MouseWheel
|
minFontSize(10).maxFontSize(75); // allow font zoom with Ctrl + MouseWheel
|
||||||
|
showModificationMarks = true;
|
||||||
|
|
||||||
_showLineNumbers = true;
|
_showLineNumbers = true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue