diff --git a/src/dlangui/core/editable.d b/src/dlangui/core/editable.d index 712dda8e..d64a8a2d 100644 --- a/src/dlangui/core/editable.d +++ b/src/dlangui/core/editable.d @@ -199,6 +199,10 @@ struct TextRange { bool isInside(TextPosition p) const { return start <= p && end > p; } + /// returns true if position is inside this range or right after this range + bool isInsideOrNext(TextPosition p) const { + return start <= p && end >= p; + } /// returns true if range is empty @property bool empty() const { return end <= start; @@ -768,7 +772,7 @@ class EditableContent { } - /// returns line text + /// returns line count @property int length() { return cast(int)_lines.length; } dstring opIndex(int index) { return line(index); diff --git a/src/dlangui/widgets/editors.d b/src/dlangui/widgets/editors.d index 4b25689f..c8672083 100644 --- a/src/dlangui/widgets/editors.d +++ b/src/dlangui/widgets/editors.d @@ -247,9 +247,10 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction protected bool _replaceMode; - // TODO: move to styles protected uint _selectionColorFocused = 0xB060A0FF; protected uint _selectionColorNormal = 0xD060A0FF; + protected uint _searchHighlightColorCurrent = 0x808080FF; + protected uint _searchHighlightColorOther = 0xC08080FF; protected uint _leftPaneBackgroundColor = 0xF4F4F4; protected uint _leftPaneBackgroundColor2 = 0xFFFFFF; protected uint _leftPaneBackgroundColor3 = 0xF8F8F8; @@ -1037,6 +1038,8 @@ class EditWidgetBase : ScrollWidgetBase, EditableContentListener, MenuItemAction _caretColorReplace = style.customColor("edit_caret_replace"); _selectionColorFocused = style.customColor("editor_selection_focused"); _selectionColorNormal = style.customColor("editor_selection_normal"); + _searchHighlightColorCurrent = style.customColor("editor_search_highlight_current"); + _searchHighlightColorOther = style.customColor("editor_search_highlight_other"); _leftPaneBackgroundColor = style.customColor("editor_left_pane_background"); _leftPaneBackgroundColor2 = style.customColor("editor_left_pane_background2"); _leftPaneBackgroundColor3 = style.customColor("editor_left_pane_background3"); @@ -2687,6 +2690,106 @@ class EditBox : EditWidgetBase { super.measure(parentWidth, parentHeight); } + protected dstring _textToHighlight; + protected bool _textToHighlightCaseSensitive; + /// text pattern to highlight - e.g. for search + @property dstring textToHighlight() { + return _textToHighlight; + } + /// set text to highlight -- e.g. for search + void setTextToHighlight(dstring pattern, bool caseSensitive) { + _textToHighlight = pattern; + _textToHighlightCaseSensitive = caseSensitive; + invalidate(); + } + + protected void highlightTextPattern(DrawBuf buf, int lineIndex, Rect lineRect, Rect visibleRect) { + if (!_textToHighlight.length) + return; + dstring lineText = _content.line(lineIndex); + if (lineText.length < _textToHighlight.length) + return; + ptrdiff_t start = 0; + import std.string : indexOf, CaseSensitive, Yes, No; + for (;;) { + ptrdiff_t pos = lineText[start .. $].indexOf(_textToHighlight, _textToHighlightCaseSensitive ? Yes.caseSensitive : No.caseSensitive); + if (pos < 0) + break; + // found text to highlight + start += pos; + TextRange r = TextRange(TextPosition(lineIndex, cast(int)start), TextPosition(lineIndex, cast(int)(start + _textToHighlight.length))); + start += _textToHighlight.length; + uint color = r.isInsideOrNext(caretPos) ? _searchHighlightColorCurrent : _searchHighlightColorOther; + highlightLineRange(buf, lineRect, color, r); + } + } + + /// find all occurences of text pattern in content + TextRange[] findAll(dstring pattern, bool caseSensitive) { + TextRange[] res; + res.assumeSafeAppend(); + if (!pattern.length) + return res; + import std.string : indexOf, CaseSensitive, Yes, No; + for (int i = 0; i < _content.length; i++) { + dstring lineText = _content.line(i); + if (lineText.length < pattern.length) + continue; + ptrdiff_t start = 0; + for (;;) { + ptrdiff_t pos = lineText[start .. $].indexOf(_textToHighlight, _textToHighlightCaseSensitive ? Yes.caseSensitive : No.caseSensitive); + if (pos < 0) + break; + // found text to highlight + start += pos; + TextRange r = TextRange(TextPosition(i, cast(int)start), TextPosition(i, cast(int)(start + _textToHighlight.length))); + res ~= r; + start += _textToHighlight.length; + } + } + return res; + } + + /// find next occurence of text pattern in content, returns true if found + bool findNextPattern(ref TextPosition pos, dstring pattern, bool caseSensitive, int direction) { + TextRange[] all = findAll(pattern, caseSensitive); + if (!all.length) + return false; + int currentIndex = -1; + int nearestIndex = -1; + for (int i = 0; i < all.length; i++) { + if (all[i].isInsideOrNext(pos)) { + currentIndex = i; + break; + } + } + for (int i = 0; i < all.length; i++) { + if (pos < all[i].start) { + nearestIndex = i; + break; + } + if (pos > all[i].end) { + nearestIndex = i + 1; + } + } + if (currentIndex >= 0) { + if (all.length < 2 && direction != 0) + return false; + currentIndex += direction; + if (currentIndex < 0) + currentIndex = cast(int)all.length - 1; + else if (currentIndex >= all.length) + currentIndex = 0; + pos = all[currentIndex].start; + return true; + } + if (direction >= 0) + nearestIndex++; + if (nearestIndex >= all.length) + nearestIndex = 0; + pos = all[nearestIndex].start; + return true; + } protected void highlightLineRange(DrawBuf buf, Rect lineRect, uint color, TextRange r) { Rect startrc = textPosToClient(r.start); @@ -2721,6 +2824,8 @@ class EditBox : EditWidgetBase { } } + highlightTextPattern(buf, lineIndex, lineRect, visibleRect); + if (_matchingBraces.start.line == lineIndex) { TextRange r = TextRange(_matchingBraces.start, _matchingBraces.start.offset(1)); highlightLineRange(buf, lineRect, _matchingBracketHightlightColor, r); @@ -3255,6 +3360,7 @@ class FindPanel : HorizontalLayout { } void close() { + _editor.setTextToHighlight(null, false); _editor.closeFindPanel(); } @@ -3282,6 +3388,7 @@ class FindPanel : HorizontalLayout { setDirection(back); dstring currentText = _edFind.text; Log.d("findNext text=", currentText, " back=", back); + _editor.setTextToHighlight(currentText, _cbCaseSensitive.checked); } void onFindTextChange(EditableContent source) { diff --git a/views/res/theme_dark.xml b/views/res/theme_dark.xml index 663c164c..6e848363 100644 --- a/views/res/theme_dark.xml +++ b/views/res/theme_dark.xml @@ -10,6 +10,8 @@ + + diff --git a/views/res/theme_default.xml b/views/res/theme_default.xml index c3b1fa70..525d8c51 100644 --- a/views/res/theme_default.xml +++ b/views/res/theme_default.xml @@ -12,6 +12,8 @@ + +