mirror of https://github.com/buggins/dlangide.git
scroll and cursor position
This commit is contained in:
parent
f65108dbea
commit
c4869ca3c0
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue