scroll and cursor position

This commit is contained in:
Vadim Lopatin 2016-05-30 12:03:41 +03:00
parent f65108dbea
commit c4869ca3c0
1 changed files with 128 additions and 11 deletions

View File

@ -3,12 +3,41 @@ module dlangide.ui.terminal;
import dlangui.widgets.widget; import dlangui.widgets.widget;
import dlangui.widgets.controls; import dlangui.widgets.controls;
struct TerminalChar { struct TerminalAttr {
ubyte bgColor = 0; ubyte bgColor = 7;
ubyte textColor = 0; ubyte textColor = 0;
}
struct TerminalChar {
TerminalAttr attr;
dchar ch = ' '; dchar ch = ' ';
} }
__gshared static uint[16] TERMINAL_PALETTE = [
0x000000, // black
0xFF0000,
0x00FF00,
0xFFFF00,
0x0000FF,
0xFF00FF,
0x00FFFF,
0xFFFFFF, // white
0x808080,
0x800000,
0x008000,
0x808000,
0x000080,
0x800080,
0x008080,
0xC0C0C0,
];
uint attrToColor(ubyte v) {
if (v >= 16)
return 0;
return TERMINAL_PALETTE[v];
}
struct TerminalLine { struct TerminalLine {
TerminalChar[] line; TerminalChar[] line;
bool overflowFlag; bool overflowFlag;
@ -20,13 +49,17 @@ struct TerminalLine {
} }
void markLineOverflow() {} void markLineOverflow() {}
void markLineEol() {} void markLineEol() {}
void putCharAt(dchar ch, int x) { void putCharAt(dchar ch, int x, TerminalAttr currentAttr) {
if (x >= line.length) { if (x >= line.length) {
TerminalChar d;
d.attr = currentAttr;
d.ch = ' ';
while (x >= line.length) { while (x >= line.length) {
line.assumeSafeAppend; line.assumeSafeAppend;
line ~= TerminalChar.init; line ~= d;
} }
} }
line[x].attr = currentAttr;
line[x].ch = ch; line[x].ch = ch;
} }
} }
@ -35,6 +68,7 @@ struct TerminalContent {
TerminalLine[] lines; TerminalLine[] lines;
Rect rc; Rect rc;
FontRef font; FontRef font;
TerminalAttr currentAttr;
int maxBufferLines = 3000; int maxBufferLines = 3000;
int topLine; int topLine;
int width; // width in chars int width; // width in chars
@ -43,6 +77,7 @@ struct TerminalContent {
int charh; // single char height int charh; // single char height
int cursorx; int cursorx;
int cursory; int cursory;
bool focused;
void layout(FontRef font, Rect rc) { void layout(FontRef font, Rect rc) {
this.rc = rc; this.rc = rc;
this.font = font; this.font = font;
@ -65,15 +100,35 @@ struct TerminalContent {
dchar[] text; dchar[] text;
text.length = 1; text.length = 1;
text[0] = ' '; text[0] = ' ';
int screenTopLine = cast(int)lines.length - height;
if (screenTopLine < 0)
screenTopLine = 0;
for (uint i = 0; i < height && i + topLine < lines.length; i++) { for (uint i = 0; i < height && i + topLine < lines.length; i++) {
lineRect.bottom = lineRect.top + charh; lineRect.bottom = lineRect.top + charh;
TerminalLine * p = &lines[i + topLine]; TerminalLine * p = &lines[i + topLine];
// draw line in rect // draw line in rect
for (int x = 0; x < p.line.length; x++) { for (int x = 0; x < width; x++) {
dchar ch = p.line[x].ch; bool isCursorPos = x == cursorx && i + topLine == cursory + screenTopLine;
if (ch >= ' ') { TerminalChar ch = x < p.line.length ? p.line[x] : TerminalChar.init;
text[0] = ch; uint bgcolor = attrToColor(ch.attr.bgColor);
font.drawText(buf, lineRect.left + x * charw, lineRect.top, text, 0); uint textcolor = attrToColor(ch.attr.textColor);
if (isCursorPos && focused) {
// invert
uint tmp = bgcolor;
bgcolor = textcolor;
textcolor = tmp;
}
Rect charrc = lineRect;
charrc.left = lineRect.left + x * charw;
charrc.right = charrc.left + charw;
charrc.bottom = charrc.top + charh;
buf.fillRect(charrc, bgcolor);
if (isCursorPos) {
buf.drawFrame(charrc, focused ? (textcolor | 0xC0000000) : (textcolor | 0x80000000), Rect(1,1,1,1));
}
if (ch.ch >= ' ') {
text[0] = ch.ch;
font.drawText(buf, charrc.left, charrc.top, text, textcolor);
} }
} }
lineRect.top = lineRect.bottom; lineRect.top = lineRect.bottom;
@ -119,7 +174,7 @@ struct TerminalContent {
line = getLine(y); line = getLine(y);
x = 0; x = 0;
} }
line.putCharAt(ch, x); line.putCharAt(ch, x, currentAttr);
} }
int tabSize = 8; int tabSize = 8;
// supports printed characters and \r \n \t // supports printed characters and \r \n \t
@ -161,9 +216,18 @@ struct TerminalContent {
sb.maxValue = lines.length ? lines.length - 1 : 0; sb.maxValue = lines.length ? lines.length - 1 : 0;
sb.position = topLine; sb.position = topLine;
} }
void scrollTo(int y) {
if (y + height > lines.length)
y = cast(int)lines.length - height;
if (y < 0)
y = 0;
topLine = y;
}
} }
class TerminalWidget : WidgetGroup { class TerminalWidget : WidgetGroup, OnScrollHandler {
protected ScrollBar _verticalScrollBar; protected ScrollBar _verticalScrollBar;
protected TerminalContent _content; protected TerminalContent _content;
this() { this() {
@ -172,10 +236,52 @@ class TerminalWidget : WidgetGroup {
this(string ID) { this(string ID) {
super(ID); super(ID);
styleId = "TERMINAL"; styleId = "TERMINAL";
focusable = true;
_verticalScrollBar = new ScrollBar("VERTICAL_SCROLLBAR", Orientation.Vertical); _verticalScrollBar = new ScrollBar("VERTICAL_SCROLLBAR", Orientation.Vertical);
_verticalScrollBar.minValue = 0; _verticalScrollBar.minValue = 0;
_verticalScrollBar.scrollEvent = this;
addChild(_verticalScrollBar); addChild(_verticalScrollBar);
} }
void scrollTo(int y) {
_content.scrollTo(y);
}
/// handle scroll event
bool onScrollEvent(AbstractSlider source, ScrollEvent event) {
switch(event.action) {
/// space above indicator pressed
case ScrollAction.PageUp:
scrollTo(_content.topLine - (_content.height ? _content.height - 1 : 1));
break;
/// space below indicator pressed
case ScrollAction.PageDown:
scrollTo(_content.topLine + (_content.height ? _content.height - 1 : 1));
break;
/// up/left button pressed
case ScrollAction.LineUp:
scrollTo(_content.topLine - 1);
break;
/// down/right button pressed
case ScrollAction.LineDown:
scrollTo(_content.topLine + 1);
break;
/// slider pressed
case ScrollAction.SliderPressed:
break;
/// dragging in progress
case ScrollAction.SliderMoved:
scrollTo(event.position);
break;
/// dragging finished
case ScrollAction.SliderReleased:
break;
default:
break;
}
return true;
}
/** /**
Measure widget according to desired width and height constraints. (Step 1 of two phase layout). Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
@ -302,4 +408,15 @@ class TerminalWidget : WidgetGroup {
} }
_content.updateScrollBar(_verticalScrollBar); _content.updateScrollBar(_verticalScrollBar);
} }
/// override to handle focus changes
override protected void handleFocusChange(bool focused, bool receivedFocusFromKeyboard = false) {
if (focused)
_content.focused = true;
else {
_content.focused = false;
}
super.handleFocusChange(focused);
}
} }