line numbers support in editors

This commit is contained in:
Vadim Lopatin 2015-01-19 18:24:35 +03:00
parent d634aed027
commit 5ab9280c46
6 changed files with 169 additions and 6 deletions

View File

@ -66,7 +66,7 @@
<debuglevel>0</debuglevel>
<debugids>DebugFocus</debugids>
<versionlevel>0</versionlevel>
<versionids>Unicode</versionids>
<versionids>Unicode USE_SDL USE_OPENGL</versionids>
<dump_source>0</dump_source>
<mapverbosity>0</mapverbosity>
<createImplib>1</createImplib>

View File

@ -66,7 +66,7 @@
<debuglevel>0</debuglevel>
<debugids />
<versionlevel>0</versionlevel>
<versionids>Unicode</versionids>
<versionids>Unicode USE_SDL USE_OPENGL</versionids>
<dump_source>0</dump_source>
<mapverbosity>3</mapverbosity>
<createImplib>0</createImplib>

View File

@ -141,6 +141,7 @@ Widget createEditorSettingsControl(EditWidgetBase editor) {
res.addChild((new CheckBox("wantTabs", "wantTabs"d)).checked(editor.wantTabs).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.wantTabs = checked; return true;}));
res.addChild((new CheckBox("useSpacesForTabs", "useSpacesForTabs"d)).checked(editor.useSpacesForTabs).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.useSpacesForTabs = checked; return true;}));
res.addChild((new CheckBox("readOnly", "readOnly"d)).checked(editor.readOnly).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.readOnly = checked; return true;}));
res.addChild((new CheckBox("showLineNumbers", "showLineNumbers"d)).checked(editor.showLineNumbers).addOnCheckChangeListener(delegate(Widget, bool checked) { editor.showLineNumbers = checked; return true;}));
res.addChild((new CheckBox("fixedFont", "fixedFont"d)).checked(editor.fontFamily == FontFamily.MonoSpace).addOnCheckChangeListener(delegate(Widget, bool checked) {
if (checked)
editor.fontFamily(FontFamily.MonoSpace).fontFace("Courier New");

View File

@ -936,19 +936,114 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
protected bool _fixedFont;
protected int _spaceWidth;
protected int _tabSize = 4;
protected int _leftPaneWidth; // left pane - can be used to show line numbers, collapse controls, bookmarks, breakpoints, custom icons
protected int _minFontSize = -1; // disable zooming
protected int _maxFontSize = -1; // disable zooming
protected bool _wantTabs = true;
protected bool _useSpacesForTabs = false;
protected bool _showLineNumbers = false; // show line numbers in left pane
protected bool _showModificationMarks = false; // show modification marks in left pane
protected bool _showIcons = false; // show icons in left pane
protected bool _showFolding = false; // show folding controls in left pane
protected int _lineNumbersWidth = 0;
protected int _modificationMarksWidth = 0;
protected int _iconsWidth = 0;
protected int _foldingWidth = 0;
protected bool _replaceMode;
// TODO: move to styles
protected uint _selectionColorFocused = 0xB060A0FF;
protected uint _selectionColorNormal = 0xD060A0FF;
protected uint _leftPaneBackgroundColor = 0xE0E0E0;
protected uint _leftPaneBackgroundColor2 = 0xFFFFFF;
protected uint _leftPaneBackgroundColor3 = 0xC0C0C0;
protected uint _leftPaneLineNumberColor = 0x4060D0;
protected uint _leftPaneLineNumberBackgroundColor = 0xF0F0F0;
protected uint _iconsPaneWidth = 16;
protected uint _foldingPaneWidth = 16;
protected uint _modificationMarksPaneWidth = 8;
/// override to support modification of client rect after change, e.g. apply offset
override protected void handleClientRectLayout(ref Rect rc) {
updateLeftPaneWidth();
rc.left += _leftPaneWidth;
}
/// override for multiline editors
protected int lineCount() {
return 1;
}
/// override to add custom items on left panel
protected void updateLeftPaneWidth() {
_iconsWidth = _showIcons ? _iconsPaneWidth : 0;
_foldingWidth = _showFolding ? _foldingPaneWidth : 0;
_modificationMarksWidth = _showModificationMarks ? _modificationMarksPaneWidth : 0;
_lineNumbersWidth = 0;
if (_showLineNumbers) {
dchar[] s = to!(dchar[])(lineCount + 1);
foreach(ref ch; s)
ch = '9';
FontRef fnt = font;
Point sz = fnt.textSize(s);
_lineNumbersWidth = sz.x;
}
_leftPaneWidth = _lineNumbersWidth + _modificationMarksWidth + _foldingWidth + _iconsWidth;
if (_leftPaneWidth)
_leftPaneWidth += 3;
}
protected void drawLeftPaneFolding(DrawBuf buf, Rect rc, int line) {
}
protected void drawLeftPaneIcons(DrawBuf buf, Rect rc, int line) {
}
protected void drawLeftPaneModificationMarks(DrawBuf buf, Rect rc, int line) {
}
protected void drawLeftPaneLineNumbers(DrawBuf buf, Rect rc, int line) {
buf.fillRect(rc, _leftPaneLineNumberBackgroundColor);
if (line < 0)
return;
dstring s = to!dstring(line + 1);
FontRef fnt = font;
Point sz = fnt.textSize(s);
int x = rc.right - sz.x;
int y = rc.top + (rc.height - sz.y) / 2;
fnt.drawText(buf, x, y, s, _leftPaneLineNumberColor);
}
protected void drawLeftPane(DrawBuf buf, Rect rc, int line) {
// override for custom drawn left pane
buf.fillRect(rc, _leftPaneBackgroundColor);
buf.fillRect(Rect(rc.right - 2, rc.top, rc.right - 1, rc.bottom), _leftPaneBackgroundColor2);
buf.fillRect(Rect(rc.right - 1, rc.top, rc.right - 0, rc.bottom), _leftPaneBackgroundColor3);
rc.right -= 3;
if (_foldingWidth) {
Rect rc2 = rc;
rc.right = rc2.left = rc2.right - _foldingWidth;
drawLeftPaneFolding(buf, rc2, line);
}
if (_lineNumbersWidth) {
Rect rc2 = rc;
rc.right = rc2.left = rc2.right - _lineNumbersWidth;
drawLeftPaneLineNumbers(buf, rc2, line);
}
if (_modificationMarksWidth) {
Rect rc2 = rc;
rc.right = rc2.left = rc2.right - _modificationMarksWidth;
drawLeftPaneModificationMarks(buf, rc2, line);
}
if (_iconsWidth) {
Rect rc2 = rc;
rc.right = rc2.left = rc2.right - _iconsWidth;
drawLeftPaneIcons(buf, rc2, line);
}
}
this(string ID, ScrollBarMode hscrollbarMode = ScrollBarMode.Visible, ScrollBarMode vscrollbarMode = ScrollBarMode.Visible) {
super(ID, hscrollbarMode, vscrollbarMode);
@ -1108,6 +1203,21 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
return this;
}
/// when true, line numbers are shown
@property bool showLineNumbers() {
return _showLineNumbers;
}
/// when true, line numbers are shown
@property EditWidgetBase showLineNumbers(bool flg) {
if (_showLineNumbers != flg) {
_showLineNumbers = flg;
updateLeftPaneWidth();
requestLayout();
}
return this;
}
/// readonly flag (when true, user cannot change content of editor)
@property bool readOnly() {
return !enabled || _content.readOnly;
@ -1931,7 +2041,7 @@ class EditLine : EditWidgetBase {
override void measure(int parentWidth, int parentHeight) {
updateFontProps();
measureVisibleText();
measuredContent(parentWidth, parentHeight, _measuredTextSize.x, _measuredTextSize.y);
measuredContent(parentWidth, parentHeight, _measuredTextSize.x + _leftPaneWidth, _measuredTextSize.y);
}
override protected bool handleAction(const Action a) {
@ -1997,6 +2107,12 @@ class EditLine : EditWidgetBase {
// draw selection rect for line
buf.fillRect(rc, focused ? _selectionColorFocused : _selectionColorNormal);
}
if (_leftPaneWidth > 0) {
Rect leftPaneRect = visibleRect;
leftPaneRect.right = leftPaneRect.left;
leftPaneRect.left -= _leftPaneWidth;
drawLeftPane(buf, leftPaneRect, 0);
}
}
}
@ -2051,6 +2167,11 @@ class EditBox : EditWidgetBase {
protected int[][] _visibleLinesMeasurement; // char positions for visible lines
protected int[] _visibleLinesWidths; // width (in pixels) of visible lines
override protected int lineCount() {
return _content.length;
}
override protected void updateMaxLineWidth() {
// find max line width. TODO: optimize!!!
int maxw;
@ -2494,15 +2615,34 @@ class EditBox : EditWidgetBase {
if (focused && lineIndex == _caretPos.line && _selectionRange.singleLine && _selectionRange.start.line == _caretPos.line) {
buf.drawFrame(visibleRect, 0xA0808080, Rect(1,1,1,1));
}
}
override protected void drawExtendedArea(DrawBuf buf) {
if (_leftPaneWidth <= 0)
return;
Rect rc = _clientRect;
FontRef font = font();
int i = _firstVisibleLine;
int lc = lineCount;
for (;;) {
Rect lineRect = rc;
lineRect.left = _clientRect.left - _leftPaneWidth;
lineRect.right = _clientRect.left;
lineRect.bottom = lineRect.top + _lineHeight;
if (lineRect.top >= _clientRect.bottom)
break;
drawLeftPane(buf, lineRect, i < lc ? i : -1);
i++;
rc.top += _lineHeight;
}
}
override protected void drawClient(DrawBuf buf) {
Rect rc = _clientRect;
FontRef font = font();
//dstring txt = text;
//Point sz = font.textSize(txt);
//font.drawText(buf, rc.left, rc.top + sz.y / 10, txt, textColor);
for (int i = 0; i < _visibleLines.length; i++) {
dstring txt = _visibleLines[i];
Rect lineRect = rc;
@ -2514,6 +2654,12 @@ class EditBox : EditWidgetBase {
visibleRect.left = _clientRect.left;
visibleRect.right = _clientRect.right;
drawLineBackground(buf, _firstVisibleLine + i, lineRect, visibleRect);
if (_leftPaneWidth > 0) {
Rect leftPaneRect = visibleRect;
leftPaneRect.right = leftPaneRect.left;
leftPaneRect.left -= _leftPaneWidth;
drawLeftPane(buf, leftPaneRect, 0);
}
if (txt.length > 0) {
font.drawText(buf, rc.left - _scrollPos.x, rc.top + i * _lineHeight, txt, textColor, tabSize);
}

View File

@ -169,6 +169,9 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
// override it
}
protected void drawExtendedArea(DrawBuf buf) {
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
@ -192,6 +195,13 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
auto saver2 = ClipRectSaver(buf, _clientRect, alpha);
drawClient(buf);
}
{
// no clipping for drawing of extended area
Rect clipr = rc;
clipr.bottom = _clientRect.bottom;
auto saver3 = ClipRectSaver(buf, clipr, alpha);
drawExtendedArea(buf);
}
}
_needDraw = false;
@ -234,6 +244,10 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
}
/// override to support modification of client rect after change, e.g. apply offset
protected void handleClientRectLayout(ref Rect rc) {
}
/// Set widget rectangle to specified value and layout widget contents. (Step 2 of two phase layout).
override void layout(Rect rc) {
if (visibility == Visibility.Gone) {
@ -271,6 +285,7 @@ class ScrollWidgetBase : WidgetGroup, OnScrollHandler {
}
// client area
_clientRect = rc;
handleClientRectLayout(_clientRect);
if (needVscroll)
_clientRect.right = vsbrc.left;
if (needHscroll)

View File

@ -30,6 +30,7 @@ class SourceEdit : EditBox {
fontSize = 18;
layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
minFontSize(12).maxFontSize(75); // allow font zoom with Ctrl + MouseWheel
_showLineNumbers = true;
}
this() {
this("SRCEDIT");