support multiple views for same EditableContent

This commit is contained in:
Vadim Lopatin 2014-04-24 15:48:34 +04:00
parent ec31e0838c
commit 8b0570b22e
1 changed files with 44 additions and 32 deletions

View File

@ -258,8 +258,9 @@ class UndoBuffer {
} }
} }
/// Editable Content change listener
interface EditableContentListener { interface EditableContentListener {
bool onContentChange(EditableContent content, EditOperation operation, ref TextRange rangeBefore, ref TextRange rangeAfter); bool onContentChange(EditableContent content, EditOperation operation, ref TextRange rangeBefore, ref TextRange rangeAfter, Object source);
} }
/// editable plain text (singleline/multiline) /// editable plain text (singleline/multiline)
@ -332,8 +333,8 @@ class EditableContent {
return m; return m;
} }
bool handleContentChange(EditOperation op, ref TextRange rangeBefore, ref TextRange rangeAfter) { bool handleContentChange(EditOperation op, ref TextRange rangeBefore, ref TextRange rangeAfter, Object source) {
return contentChangeListeners(this, op, rangeBefore, rangeAfter); return contentChangeListeners(this, op, rangeBefore, rangeAfter, source);
} }
/// return text for specified range /// return text for specified range
@ -415,7 +416,7 @@ class EditableContent {
} }
} }
bool performOperation(EditOperation op) { bool performOperation(EditOperation op, Object source) {
if (op.action == EditAction.Replace) { if (op.action == EditAction.Replace) {
TextRange rangeBefore = op.range; TextRange rangeBefore = op.range;
dstring[] oldcontent = rangeText(rangeBefore); dstring[] oldcontent = rangeText(rangeBefore);
@ -435,7 +436,7 @@ class EditableContent {
op.newRange = rangeAfter; op.newRange = rangeAfter;
op.oldContent = oldcontent; op.oldContent = oldcontent;
replaceRange(rangeBefore, rangeAfter, newcontent); replaceRange(rangeBefore, rangeAfter, newcontent);
handleContentChange(op, rangeBefore, rangeAfter); handleContentChange(op, rangeBefore, rangeAfter, source);
_undoBuffer.saveForUndo(op); _undoBuffer.saveForUndo(op);
return true; return true;
} }
@ -461,7 +462,7 @@ class EditableContent {
TextRange rangeAfter = op.range; TextRange rangeAfter = op.range;
//Log.d("Undoing op rangeBefore=", rangeBefore, " contentBefore=`", oldcontent, "` rangeAfter=", rangeAfter, " contentAfter=`", newcontent, "`"); //Log.d("Undoing op rangeBefore=", rangeBefore, " contentBefore=`", oldcontent, "` rangeAfter=", rangeAfter, " contentAfter=`", newcontent, "`");
replaceRange(rangeBefore, rangeAfter, newcontent); replaceRange(rangeBefore, rangeAfter, newcontent);
handleContentChange(op, rangeBefore, rangeAfter); handleContentChange(op, rangeBefore, rangeAfter, this);
return true; return true;
} }
/// redoes last undone change /// redoes last undone change
@ -475,7 +476,7 @@ class EditableContent {
TextRange rangeAfter = op.newRange; TextRange rangeAfter = op.newRange;
//Log.d("Redoing op rangeBefore=", rangeBefore, " contentBefore=`", oldcontent, "` rangeAfter=", rangeAfter, " contentAfter=`", newcontent, "`"); //Log.d("Redoing op rangeBefore=", rangeBefore, " contentBefore=`", oldcontent, "` rangeAfter=", rangeAfter, " contentAfter=`", newcontent, "`");
replaceRange(rangeBefore, rangeAfter, newcontent); replaceRange(rangeBefore, rangeAfter, newcontent);
handleContentChange(op, rangeBefore, rangeAfter); handleContentChange(op, rangeBefore, rangeAfter, this);
return true; return true;
} }
/// clear undo/redp history /// clear undo/redp history
@ -594,6 +595,11 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
protected bool _replaceMode; protected bool _replaceMode;
// TODO: move to styles
protected uint _selectionColorFocused = 0xB060A0FF;
protected uint _selectionColorNormal = 0xD060A0FF;
this(string ID) { this(string ID) {
super(ID); super(ID);
focusable = true; focusable = true;
@ -760,12 +766,17 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
protected void updateMaxLineWidth() { protected void updateMaxLineWidth() {
} }
override bool onContentChange(EditableContent content, EditOperation operation, ref TextRange rangeBefore, ref TextRange rangeAfter) { override bool onContentChange(EditableContent content, EditOperation operation, ref TextRange rangeBefore, ref TextRange rangeAfter, Object source) {
updateMaxLineWidth(); updateMaxLineWidth();
measureVisibleText(); measureVisibleText();
_caretPos = rangeAfter.end; if (source is this) {
_selectionRange.start = _caretPos; _caretPos = rangeAfter.end;
_selectionRange.end = _caretPos; _selectionRange.start = _caretPos;
_selectionRange.end = _caretPos;
} else {
correctCaretPos();
// TODO: do something better (e.g. take into account ranges when correcting)
}
ensureCaretVisible(); ensureCaretVisible();
invalidate(); invalidate();
return true; return true;
@ -1014,7 +1025,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
if (!_selectionRange.empty) { if (!_selectionRange.empty) {
// clear selection // clear selection
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
ensureCaretVisible(); ensureCaretVisible();
return true; return true;
} }
@ -1024,7 +1035,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
TextRange range = TextRange(_caretPos, _caretPos); TextRange range = TextRange(_caretPos, _caretPos);
range.start.pos--; range.start.pos--;
EditOperation op = new EditOperation(EditAction.Replace, range, [""d]); EditOperation op = new EditOperation(EditAction.Replace, range, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
} else if (_caretPos.line > 0) { } else if (_caretPos.line > 0) {
// merge with previous line // merge with previous line
TextRange range = TextRange(_caretPos, _caretPos); TextRange range = TextRange(_caretPos, _caretPos);
@ -1032,7 +1043,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
dstring prevLine = _content[range.start.line]; dstring prevLine = _content[range.start.line];
range.start.pos = cast(int)prevLine.length; range.start.pos = cast(int)prevLine.length;
EditOperation op = new EditOperation(EditAction.Replace, range, [""d]); EditOperation op = new EditOperation(EditAction.Replace, range, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
return true; return true;
case EditorActions.DelNextChar: case EditorActions.DelNextChar:
@ -1040,7 +1051,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
return true; return true;
if (!_selectionRange.empty) { if (!_selectionRange.empty) {
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
return true; return true;
} }
correctCaretPos(); correctCaretPos();
@ -1049,14 +1060,14 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
TextRange range = TextRange(_caretPos, _caretPos); TextRange range = TextRange(_caretPos, _caretPos);
range.end.pos++; range.end.pos++;
EditOperation op = new EditOperation(EditAction.Replace, range, [""d]); EditOperation op = new EditOperation(EditAction.Replace, range, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
} else if (_caretPos.line < _content.length - 1) { } else if (_caretPos.line < _content.length - 1) {
// merge with next line // merge with next line
TextRange range = TextRange(_caretPos, _caretPos); TextRange range = TextRange(_caretPos, _caretPos);
range.end.line++; range.end.line++;
range.end.pos = 0; range.end.pos = 0;
EditOperation op = new EditOperation(EditAction.Replace, range, [""d]); EditOperation op = new EditOperation(EditAction.Replace, range, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
return true; return true;
case EditorActions.Copy: case EditorActions.Copy:
@ -1072,7 +1083,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
if (readOnly) if (readOnly)
return true; return true;
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
return true; return true;
case EditorActions.Paste: case EditorActions.Paste:
@ -1087,7 +1098,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
lines = [replaceEolsWithSpaces(selectionText)]; lines = [replaceEolsWithSpaces(selectionText)];
} }
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, lines); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, lines);
_content.performOperation(op); _content.performOperation(op, this);
} }
return true; return true;
case EditorActions.Undo: case EditorActions.Undo:
@ -1112,11 +1123,11 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
if (_useSpacesForTabs) { if (_useSpacesForTabs) {
// insert one or more spaces to // insert one or more spaces to
EditOperation op = new EditOperation(EditAction.Replace, TextRange(_caretPos, _caretPos), [spacesForTab(_caretPos.pos)]); EditOperation op = new EditOperation(EditAction.Replace, TextRange(_caretPos, _caretPos), [spacesForTab(_caretPos.pos)]);
_content.performOperation(op); _content.performOperation(op, this);
} else { } else {
// just insert tab character // just insert tab character
EditOperation op = new EditOperation(EditAction.Replace, TextRange(_caretPos, _caretPos), ["\t"d]); EditOperation op = new EditOperation(EditAction.Replace, TextRange(_caretPos, _caretPos), ["\t"d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
} else { } else {
if (wholeLinesSelected()) { if (wholeLinesSelected()) {
@ -1127,11 +1138,11 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
if (_useSpacesForTabs) { if (_useSpacesForTabs) {
// insert one or more spaces to // insert one or more spaces to
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [spacesForTab(_selectionRange.start.pos)]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [spacesForTab(_selectionRange.start.pos)]);
_content.performOperation(op); _content.performOperation(op, this);
} else { } else {
// just insert tab character // just insert tab character
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, ["\t"d]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, ["\t"d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
} }
@ -1147,7 +1158,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
TextRange r = spaceBefore(_caretPos); TextRange r = spaceBefore(_caretPos);
if (!r.empty) { if (!r.empty) {
EditOperation op = new EditOperation(EditAction.Replace, r, [""d]); EditOperation op = new EditOperation(EditAction.Replace, r, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
} else { } else {
if (wholeLinesSelected()) { if (wholeLinesSelected()) {
@ -1161,7 +1172,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
TextRange saveRange = _selectionRange; TextRange saveRange = _selectionRange;
TextPosition saveCursor = _caretPos; TextPosition saveCursor = _caretPos;
EditOperation op = new EditOperation(EditAction.Replace, r, [""d]); EditOperation op = new EditOperation(EditAction.Replace, r, [""d]);
_content.performOperation(op); _content.performOperation(op, this);
if (saveCursor.line == saveRange.start.line) if (saveCursor.line == saveRange.start.line)
saveCursor.pos -= nchars; saveCursor.pos -= nchars;
if (saveRange.end.line == saveRange.start.line) if (saveRange.end.line == saveRange.start.line)
@ -1264,7 +1275,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
TextRange saveRange = _selectionRange; TextRange saveRange = _selectionRange;
TextPosition saveCursor = _caretPos; TextPosition saveCursor = _caretPos;
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, newContent); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, newContent);
_content.performOperation(op); _content.performOperation(op, this);
_selectionRange = saveRange; _selectionRange = saveRange;
_caretPos = saveCursor; _caretPos = saveCursor;
ensureCaretVisible(); ensureCaretVisible();
@ -1292,10 +1303,10 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
TextRange range = _selectionRange; TextRange range = _selectionRange;
range.end.pos += cast(int)event.text.length; range.end.pos += cast(int)event.text.length;
EditOperation op = new EditOperation(EditAction.Replace, range, [event.text]); EditOperation op = new EditOperation(EditAction.Replace, range, [event.text]);
_content.performOperation(op); _content.performOperation(op, this);
} else { } else {
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [event.text]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [event.text]);
_content.performOperation(op); _content.performOperation(op, this);
} }
} }
return true; return true;
@ -1459,6 +1470,7 @@ class EditLine : EditWidgetBase {
applyPadding(_clientRc); applyPadding(_clientRc);
} }
/// override to custom highlight of line background /// override to custom highlight of line background
protected void drawLineBackground(DrawBuf buf, Rect lineRect, Rect visibleRect) { protected void drawLineBackground(DrawBuf buf, Rect lineRect, Rect visibleRect) {
if (!_selectionRange.empty) { if (!_selectionRange.empty) {
@ -1472,7 +1484,7 @@ class EditLine : EditWidgetBase {
rc.right = endx; rc.right = endx;
if (!rc.empty) { if (!rc.empty) {
// draw selection rect for line // draw selection rect for line
buf.fillRect(rc, 0xB060A0FF); buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal);
} }
} }
} }
@ -1687,14 +1699,14 @@ class EditBox : EditWidgetBase, OnScrollHandler {
correctCaretPos(); correctCaretPos();
_caretPos.pos = 0; _caretPos.pos = 0;
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d, ""d]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d, ""d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
return true; return true;
case EditorActions.InsertNewLine: case EditorActions.InsertNewLine:
{ {
correctCaretPos(); correctCaretPos();
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d, ""d]); EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [""d, ""d]);
_content.performOperation(op); _content.performOperation(op, this);
} }
return true; return true;
case EditorActions.Up: case EditorActions.Up:
@ -1852,7 +1864,7 @@ class EditBox : EditWidgetBase, OnScrollHandler {
rc.right = endx; rc.right = endx;
if (!rc.empty) { if (!rc.empty) {
// draw selection rect for line // draw selection rect for line
buf.fillRect(rc, 0xB060A0FF); buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal);
} }
} }