editbox scrollbars

This commit is contained in:
Vadim Lopatin 2014-04-21 14:43:24 +04:00
parent 5149ccfe3d
commit 31f7507822
2 changed files with 128 additions and 7 deletions

View File

@ -139,7 +139,20 @@ extern (C) int UIAppMain(string[] args) {
tabs.addTab((new TextWidget()).id("tab3").textColor(0x00802000).text("Tab 3 contents"), "Tab 3"d);
tabs.addTab((new TextWidget()).id("tab4").textColor(0x00802000).text("Tab 4 contents some long string"), "Tab 4"d);
tabs.addTab((new TextWidget()).id("tab5").textColor(0x00802000).text("Tab 5 contents"), "Tab 5"d);
tabs.addTab((new EditBox("editbox1", "Some text\nSecond line\nYet another line"d)).layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT), "EditBox"d);
EditBox editBox = new EditBox("editbox1", "Some text\nSecond line\nYet another line"d);
editBox.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
dstring text = editBox.text;
for (int i = 0; i < 100; i++) {
text ~= "\n Line ";
text ~= to!dstring(i + 3);
text ~= " Some long long line. Blah blah blah.";
for (int j = 0; j <= i % 4; j++)
text ~= " The quick brown fox jumps over the lazy dog.";
text ~= "End of line ";
text ~= to!dstring(i + 3);
}
editBox.text = text;
tabs.addTab(editBox, "EditBox"d);
tabs.selectTab("tab1");

View File

@ -24,6 +24,8 @@ import dlangui.widgets.widget;
import dlangui.widgets.controls;
import dlangui.core.signals;
import std.algorithm;
immutable dchar EOL = '\n';
/// split dstring by delimiters
@ -141,9 +143,18 @@ class EditableContent {
dstring opIndex(int index) {
return line(index);
}
/// returns line text by index
/// returns line text by index, "" if index is out of bounds
dstring line(int index) {
return _lines[index];
return index >= 0 && index < _lines.length ? _lines[index] : ""d;
}
/// returns maximum length of line
int maxLineLength() {
int m = 0;
foreach(s; _lines)
if (m < s.length)
m = s.length;
return m;
}
bool handleContentChange(EditOperation op, ref TextRange rangeBefore, ref TextRange rangeAfter) {
@ -280,7 +291,7 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
} else if (event.action == KeyAction.Text && event.text.length) {
Log.d("text entered: ", event.text);
dchar ch = event.text[0];
if (ch != 8 && ch != '\n') { // ignore Backspace
if (ch != 8 && ch != '\n' && ch != '\r') { // ignore Backspace and Return
EditOperation op = new EditOperation(EditAction.Insert, _caretPos, event.text);
_content.performOperation(op);
return true;
@ -498,7 +509,7 @@ class EditLine : EditWidgetBase {
/// single line editor
class EditBox : EditWidgetBase {
class EditBox : EditWidgetBase, OnScrollHandler {
protected ScrollBar _hscrollbar;
protected ScrollBar _vscrollbar;
@ -510,6 +521,8 @@ class EditBox : EditWidgetBase {
text = initialContent;
_hscrollbar = new ScrollBar("hscrollbar", Orientation.Horizontal);
_vscrollbar = new ScrollBar("vscrollbar", Orientation.Vertical);
_hscrollbar.onScrollEventListener = this;
_vscrollbar.onScrollEventListener = this;
addChild(_hscrollbar);
addChild(_vscrollbar);
}
@ -519,6 +532,7 @@ class EditBox : EditWidgetBase {
protected Point _scrollPos;
protected int _firstVisibleLine;
protected int _maxLineWidth;
protected int _numVisibleLines; // number of lines visible in client area
protected dstring[] _visibleLines; // text for visible lines
protected int[][] _visibleLinesMeasurement; // char positions for visible lines
@ -535,17 +549,98 @@ class EditBox : EditWidgetBase {
_visibleLinesMeasurement.length = _numVisibleLines;
_visibleLinesWidths.length = _numVisibleLines;
for (int i = 0; i < _numVisibleLines; i++) {
_visibleLines[i] = _content[i];
_visibleLines[i] = _content[_firstVisibleLine + i];
_visibleLinesMeasurement[i].length = _visibleLines[i].length;
int charsMeasured = font.measureText(_visibleLines[i], _visibleLinesMeasurement[i], int.max);
_visibleLinesWidths[i] = charsMeasured > 0 ? _visibleLinesMeasurement[i][charsMeasured - 1] : 0;
if (sz.x < _visibleLinesWidths[i])
sz.x = _visibleLinesWidths[i]; // width - max from visible lines
}
// find max line width. TODO: optimize!!!
int[] buf;
for (int i = 0; i < _content.length; i++) {
dstring s = _content[i];
if (buf.length < s.length)
buf.length = s.length;
int charsMeasured = font.measureText(s, buf, int.max);
int w = 0;
if (charsMeasured > 0)
w = buf[charsMeasured - 1];
if (sz.x < w)
sz.x = w;
}
_maxLineWidth = sz.x;
sz.y = _lineHeight * _content.length; // height - for all lines
return sz;
}
protected void updateScrollbars() {
int visibleLines = _clientRc.height / _lineHeight; // fully visible lines
if (visibleLines < 1)
visibleLines = 1;
_vscrollbar.setRange(0, _content.length - 1);
_vscrollbar.pageSize = visibleLines;
_vscrollbar.position = _firstVisibleLine;
_hscrollbar.setRange(0, _maxLineWidth + _clientRc.width / 4);
_hscrollbar.pageSize = _clientRc.width;
_hscrollbar.position = _scrollPos.x;
}
/// handle scroll event
override bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
if (source.id.equal("hscrollbar")) {
if (_scrollPos.x != event.position) {
_scrollPos.x = event.position;
invalidate();
}
return true;
} else if (source.id.equal("vscrollbar")) {
if (_firstVisibleLine != event.position) {
_firstVisibleLine = event.position;
measureVisibleText();
invalidate();
}
return true;
}
return false;
}
protected void ensureCaretVisible() {
if (_caretPos.line >= _content.length)
_caretPos.line = _content.length - 1;
if (_caretPos.line < 0)
_caretPos.line = 0;
int visibleLines = _clientRc.height / _lineHeight; // fully visible lines
if (visibleLines < 1)
visibleLines = 1;
if (_caretPos.line < _firstVisibleLine) {
_firstVisibleLine = _caretPos.line;
measureVisibleText();
invalidate();
} else if (_caretPos.line >= _firstVisibleLine + visibleLines) {
_firstVisibleLine = _caretPos.line - visibleLines + 1;
if (_firstVisibleLine < 0)
_firstVisibleLine = 0;
measureVisibleText();
invalidate();
}
//_scrollPos
Rect rc = textPosToClient(_caretPos);
if (rc.left < 0) {
// scroll left
_scrollPos.x -= -rc.left + _clientRc.width / 4;
if (_scrollPos.x < 0)
_scrollPos.x = 0;
invalidate();
} else if (rc.left >= _clientRc.width - 10) {
// scroll right
_scrollPos.x += (rc.left - _clientRc.width) + _clientRc.width / 4;
invalidate();
}
updateScrollbars();
}
override bool onContentChange(EditableContent content, EditOperation operation, ref TextRange rangeBefore, ref TextRange rangeAfter) {
measureVisibleText();
_caretPos = rangeAfter.end;
@ -566,12 +661,14 @@ class EditBox : EditWidgetBase {
else
res.left = _visibleLinesMeasurement[lineIndex][p.pos - 1];
}
res.left -= _scrollPos.x;
res.right = res.left + 1;
return res;
}
override protected TextPosition clientToTextPos(Point pt) {
TextPosition res;
pt.x += _scrollPos.x;
int lineIndex = pt.y / _lineHeight;
if (lineIndex < 0)
lineIndex = 0;
@ -613,6 +710,7 @@ class EditBox : EditWidgetBase {
correctCaretPos();
if (_caretPos.pos > 0) {
_caretPos.pos--;
ensureCaretVisible();
invalidate();
}
return true;
@ -620,6 +718,7 @@ class EditBox : EditWidgetBase {
correctCaretPos();
if (_caretPos.pos < currentLine.length) {
_caretPos.pos++;
ensureCaretVisible();
invalidate();
}
return true;
@ -630,6 +729,7 @@ class EditBox : EditWidgetBase {
range.start.pos--;
EditOperation op = new EditOperation(EditAction.Delete, range, null);
_content.performOperation(op);
ensureCaretVisible();
}
return true;
case EditorActions.DelNextChar:
@ -639,17 +739,20 @@ class EditBox : EditWidgetBase {
range.end.pos++;
EditOperation op = new EditOperation(EditAction.Delete, range, null);
_content.performOperation(op);
ensureCaretVisible();
}
return true;
case EditorActions.Up:
if (_caretPos.line > 0) {
_caretPos.line--;
ensureCaretVisible();
invalidate();
}
return true;
case EditorActions.Down:
if (_caretPos.line < _content.length - 1) {
_caretPos.line++;
ensureCaretVisible();
invalidate();
}
return true;
@ -665,12 +768,14 @@ class EditBox : EditWidgetBase {
if (_caretPos.pos > 0 || _caretPos.line > 0) {
_caretPos.line = 0;
_caretPos.pos = 0;
ensureCaretVisible();
invalidate();
}
return true;
case EditorActions.LineBegin:
if (_caretPos.pos > 0) {
_caretPos.pos = 0;
ensureCaretVisible();
invalidate();
}
return true;
@ -678,12 +783,14 @@ class EditBox : EditWidgetBase {
if (_caretPos.line < _content.length - 1 || _caretPos.pos < _content[_content.length - 1].length) {
_caretPos.line = _content.length - 1;
_caretPos.pos = _content[_content.length - 1].length;
ensureCaretVisible();
invalidate();
}
return true;
case EditorActions.LineEnd:
if (_caretPos.pos < currentLine.length) {
_caretPos.pos = currentLine.length;
ensureCaretVisible();
invalidate();
}
return true;
@ -732,6 +839,7 @@ class EditBox : EditWidgetBase {
_clientRc.right -= vsbwidth;
_clientRc.bottom -= hsbheight;
Point textSz = measureVisibleText();
updateScrollbars();
}
/// draw content
@ -751,7 +859,7 @@ class EditBox : EditWidgetBase {
for (int i = 0; i < _visibleLines.length; i++) {
dstring txt = _visibleLines[i];
if (txt.length > 0)
font.drawText(buf, rc.left, rc.top + i * _lineHeight, txt, textColor);
font.drawText(buf, rc.left - _scrollPos.x, rc.top + i * _lineHeight, txt, textColor);
}
//buf.fillRect(_clientRc, 0x80E0E0FF); // testing clientRc