mirror of https://github.com/buggins/dlangui.git
editable - refactoring of tabs processing
This commit is contained in:
parent
ed6d2df29a
commit
183571efa0
|
@ -402,20 +402,42 @@ alias TokenPropString = ubyte[];
|
|||
|
||||
/// interface for custom syntax highlight
|
||||
interface SyntaxHighlighter {
|
||||
|
||||
/// returns editable content
|
||||
@property EditableContent content();
|
||||
/// set editable content
|
||||
@property SyntaxHighlighter content(EditableContent content);
|
||||
|
||||
/// categorize characters in content by token types
|
||||
void updateHighlight(dstring[] lines, TokenPropString[] props, int changeStartLine, int changeEndLine);
|
||||
/// return true if toggle line comment is supported for file type
|
||||
@property bool supportsToggleLineComment();
|
||||
/// return true if can toggle line comments for specified text range
|
||||
bool canToggleLineComment(EditableContent content, TextRange range);
|
||||
bool canToggleLineComment(TextRange range);
|
||||
/// toggle line comments for specified text range
|
||||
void toggleLineComment(EditableContent content, TextRange range, Object source);
|
||||
void toggleLineComment(TextRange range, Object source);
|
||||
/// return true if toggle block comment is supported for file type
|
||||
@property bool supportsToggleBlockComment();
|
||||
/// return true if can toggle block comments for specified text range
|
||||
bool canToggleBlockComment(EditableContent content, TextRange range);
|
||||
bool canToggleBlockComment(TextRange range);
|
||||
/// toggle block comments for specified text range
|
||||
void toggleBlockComment(EditableContent content, TextRange range, Object source);
|
||||
void toggleBlockComment(TextRange range, Object source);
|
||||
}
|
||||
|
||||
/// measure line text (tabs, spaces, and nonspace positions)
|
||||
struct TextLineMeasure {
|
||||
/// line length
|
||||
int len;
|
||||
/// first non-space index in line
|
||||
int firstNonSpace = -1;
|
||||
/// first non-space position according to tab size
|
||||
int firstNonSpaceX;
|
||||
/// last non-space character index in line
|
||||
int lastNonSpace = -1;
|
||||
/// last non-space position based on tab size
|
||||
int lastNonSpaceX;
|
||||
/// true if line has zero length or consists of spaces and tabs only
|
||||
@property bool empty() { return len == 0 || firstNonSpace < 0; }
|
||||
}
|
||||
|
||||
/// editable plain text (singleline/multiline)
|
||||
|
@ -442,6 +464,7 @@ class EditableContent {
|
|||
|
||||
@property EditableContent syntaxHighlighter(SyntaxHighlighter syntaxHighlighter) {
|
||||
_syntaxHighlighter = syntaxHighlighter;
|
||||
_syntaxHighlighter.content = this;
|
||||
updateTokenProps(0, cast(int)_lines.length);
|
||||
return this;
|
||||
}
|
||||
|
@ -461,6 +484,31 @@ class EditableContent {
|
|||
_readOnly = readOnly;
|
||||
}
|
||||
|
||||
protected int _tabSize = 4;
|
||||
protected bool _useSpacesForTabs = true;
|
||||
/// returns tab size (in number of spaces)
|
||||
@property int tabSize() {
|
||||
return _tabSize;
|
||||
}
|
||||
/// sets tab size (in number of spaces)
|
||||
@property EditableContent tabSize(int newTabSize) {
|
||||
if (newTabSize < 1)
|
||||
newTabSize = 1;
|
||||
else if (newTabSize > 16)
|
||||
newTabSize = 16;
|
||||
_tabSize = newTabSize;
|
||||
return this;
|
||||
}
|
||||
/// when true, spaces will be inserted instead of tabs
|
||||
@property bool useSpacesForTabs() {
|
||||
return _useSpacesForTabs;
|
||||
}
|
||||
/// set new Tab key behavior flag: when true, spaces will be inserted instead of tabs
|
||||
@property EditableContent useSpacesForTabs(bool useSpacesForTabs) {
|
||||
_useSpacesForTabs = useSpacesForTabs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// listeners for edit operations
|
||||
Signal!EditableContentListener contentChangeListeners;
|
||||
|
||||
|
@ -622,6 +670,52 @@ class EditableContent {
|
|||
return TextRange(TextPosition(lineIndex, 0), lineIndex < _lines.length - 1 ? lineBegin(lineIndex + 1) : lineEnd(lineIndex));
|
||||
}
|
||||
|
||||
/// measures line non-space start and end positions
|
||||
TextLineMeasure measureLine(int lineIndex) {
|
||||
TextLineMeasure res;
|
||||
dstring s = _lines[lineIndex];
|
||||
res.len = cast(int)s.length;
|
||||
if (lineIndex < 0 || lineIndex >= _lines.length)
|
||||
return res;
|
||||
int x = 0;
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
dchar ch = s[i];
|
||||
if (ch == ' ') {
|
||||
x++;
|
||||
} else if (ch == '\t') {
|
||||
x = (x + _tabSize) % _tabSize;
|
||||
} else {
|
||||
if (res.firstNonSpace < 0) {
|
||||
res.firstNonSpace = i;
|
||||
res.firstNonSpaceX = x;
|
||||
}
|
||||
res.lastNonSpace = i;
|
||||
res.lastNonSpaceX = x;
|
||||
x++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// return true if line with index lineIndex is empty (has length 0 or consists only of spaces and tabs)
|
||||
bool lineIsEmpty(int lineIndex) {
|
||||
if (lineIndex < 0 || lineIndex >= _lines.length)
|
||||
return true;
|
||||
dstring s = _lines[lineIndex];
|
||||
foreach(ch; s)
|
||||
if (ch != ' ' && ch != '\t')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// corrent range to cover full lines
|
||||
TextRange fullLinesRange(TextRange r) {
|
||||
r.start.pos = 0;
|
||||
if (r.end.pos > 0)
|
||||
r.end = lineBegin(r.end.line + 1);
|
||||
return r;
|
||||
}
|
||||
|
||||
/// returns position before first non-space character of line, returns 0 position if no non-space chars
|
||||
TextPosition firstNonSpace(int lineIndex) {
|
||||
dstring s = line(lineIndex);
|
||||
|
|
|
@ -180,14 +180,12 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
protected Point _scrollPos;
|
||||
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
|
||||
|
@ -564,18 +562,18 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
|
||||
/// when true, spaces will be inserted instead of tabs
|
||||
@property bool useSpacesForTabs() {
|
||||
return _useSpacesForTabs;
|
||||
return _content.useSpacesForTabs;
|
||||
}
|
||||
|
||||
/// set new Tab key behavior flag: when true, spaces will be inserted instead of tabs
|
||||
@property EditWidgetBase useSpacesForTabs(bool useSpacesForTabs) {
|
||||
_useSpacesForTabs = useSpacesForTabs;
|
||||
_content.useSpacesForTabs = useSpacesForTabs;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// returns tab size (in number of spaces)
|
||||
@property int tabSize() {
|
||||
return _tabSize;
|
||||
return _content.tabSize;
|
||||
}
|
||||
|
||||
/// sets tab size (in number of spaces)
|
||||
|
@ -584,8 +582,8 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
newTabSize = 1;
|
||||
else if (newTabSize > 16)
|
||||
newTabSize = 16;
|
||||
if (newTabSize != _tabSize) {
|
||||
_tabSize = newTabSize;
|
||||
if (newTabSize != tabSize) {
|
||||
_content.tabSize = newTabSize;
|
||||
requestLayout();
|
||||
}
|
||||
return this;
|
||||
|
@ -807,7 +805,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
protected int calcLineWidth(dstring s) {
|
||||
int w = 0;
|
||||
if (_fixedFont) {
|
||||
int tabw = _tabSize * _spaceWidth;
|
||||
int tabw = tabSize * _spaceWidth;
|
||||
// version optimized for fixed font
|
||||
for (int i = 0; i < s.length; i++) {
|
||||
if (s[i] == '\t') {
|
||||
|
@ -910,7 +908,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
case EditorActions.ToggleBlockComment:
|
||||
if (!_content.syntaxHighlighter || !_content.syntaxHighlighter.supportsToggleBlockComment)
|
||||
a.state = ACTION_STATE_INVISIBLE;
|
||||
else if (_content.syntaxHighlighter.canToggleBlockComment(_content, _selectionRange))
|
||||
else if (_content.syntaxHighlighter.canToggleBlockComment(_selectionRange))
|
||||
a.state = ACTION_STATE_ENABLED;
|
||||
else
|
||||
a.state = ACTION_STATE_DISABLE;
|
||||
|
@ -918,7 +916,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
case EditorActions.ToggleLineComment:
|
||||
if (!_content.syntaxHighlighter || !_content.syntaxHighlighter.supportsToggleLineComment)
|
||||
a.state = ACTION_STATE_INVISIBLE;
|
||||
else if (_content.syntaxHighlighter.canToggleLineComment(_content, _selectionRange))
|
||||
else if (_content.syntaxHighlighter.canToggleLineComment(_selectionRange))
|
||||
a.state = ACTION_STATE_ENABLED;
|
||||
else
|
||||
a.state = ACTION_STATE_DISABLE;
|
||||
|
@ -1142,7 +1140,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
if (readOnly)
|
||||
return true;
|
||||
if (_selectionRange.empty) {
|
||||
if (_useSpacesForTabs) {
|
||||
if (useSpacesForTabs) {
|
||||
// insert one or more spaces to
|
||||
EditOperation op = new EditOperation(EditAction.Replace, TextRange(_caretPos, _caretPos), [spacesForTab(_caretPos.pos)]);
|
||||
_content.performOperation(op, this);
|
||||
|
@ -1157,7 +1155,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
return handleAction(new Action(EditorActions.Indent));
|
||||
} else {
|
||||
// insert tab
|
||||
if (_useSpacesForTabs) {
|
||||
if (useSpacesForTabs) {
|
||||
// insert one or more spaces to
|
||||
EditOperation op = new EditOperation(EditAction.Replace, _selectionRange, [spacesForTab(_selectionRange.start.pos)]);
|
||||
_content.performOperation(op, this);
|
||||
|
@ -1286,7 +1284,7 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction
|
|||
return src[unindentPos .. $].dup;
|
||||
} else {
|
||||
// indent
|
||||
if (_useSpacesForTabs) {
|
||||
if (useSpacesForTabs) {
|
||||
if (cursor > 0)
|
||||
cursorPos.pos += tabSize;
|
||||
return spacesForTab(0) ~ src;
|
||||
|
@ -2039,12 +2037,12 @@ class EditBox : EditWidgetBase {
|
|||
}
|
||||
return true;
|
||||
case EditorActions.ToggleBlockComment:
|
||||
if (_content.syntaxHighlighter && _content.syntaxHighlighter.supportsToggleBlockComment && _content.syntaxHighlighter.canToggleBlockComment(_content, _selectionRange))
|
||||
_content.syntaxHighlighter.toggleBlockComment(_content, _selectionRange, this);
|
||||
if (_content.syntaxHighlighter && _content.syntaxHighlighter.supportsToggleBlockComment && _content.syntaxHighlighter.canToggleBlockComment(_selectionRange))
|
||||
_content.syntaxHighlighter.toggleBlockComment(_selectionRange, this);
|
||||
return true;
|
||||
case EditorActions.ToggleLineComment:
|
||||
if (_content.syntaxHighlighter && _content.syntaxHighlighter.supportsToggleLineComment && _content.syntaxHighlighter.canToggleLineComment(_content, _selectionRange))
|
||||
_content.syntaxHighlighter.toggleLineComment(_content, _selectionRange, this);
|
||||
if (_content.syntaxHighlighter && _content.syntaxHighlighter.supportsToggleLineComment && _content.syntaxHighlighter.canToggleLineComment(_selectionRange))
|
||||
_content.syntaxHighlighter.toggleLineComment(_selectionRange, this);
|
||||
return true;
|
||||
case EditorActions.InsertLine:
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue