diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2021-10-10 02:02:10 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2021-10-10 02:02:10 +0300 |
commit | 2bcf60285e1c196bf05cd47a8e04beb150b485ef (patch) | |
tree | 1d48f8db0b5f42725998fef197bcb762a9a64fb2 | |
parent | 371b6e510e62d7d3ca5fd5133f190ef6a7bb72ff (diff) | |
download | scintilla-mirror-2bcf60285e1c196bf05cd47a8e04beb150b485ef.tar.gz |
Added Scintilla::Curses to allow for terminal drawing of the main caret.
This is the patch from scinterm/patches/02-caretstyle_curses.patch.
-rw-r--r-- | doc/ScintillaDoc.html | 11 | ||||
-rw-r--r-- | include/Scintilla.h | 1 | ||||
-rw-r--r-- | include/ScintillaTypes.h | 1 | ||||
-rw-r--r-- | src/EditView.cxx | 31 | ||||
-rw-r--r-- | src/Editor.cxx | 2 | ||||
-rw-r--r-- | src/PositionCache.cxx | 36 | ||||
-rw-r--r-- | src/ViewStyle.cxx | 22 | ||||
-rw-r--r-- | src/ViewStyle.h | 5 |
8 files changed, 90 insertions, 19 deletions
diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index 37a99a0f2..9f72e5fa0 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -3797,8 +3797,8 @@ struct Sci_TextToFind { <p><b id="SCI_SETCARETSTYLE">SCI_SETCARETSTYLE(int caretStyle)</b><br /> <b id="SCI_GETCARETSTYLE">SCI_GETCARETSTYLE → int</b><br /> The style of the caret can be set with <code>SCI_SETCARETSTYLE</code>. - There are separate styles for insert mode (lower 4-bits, <code>CARETSTYLE_INS_MASK</code>) and - overtype mode (bit 4). + There are separate styles for insert mode (lower 4-bits, CARETSTYLE_INS_MASK), + overtype mode (bit 4), and curses mode (bit 5). <table class="standard" summary="Caret Styles"> <tbody valign="top"> @@ -3828,6 +3828,13 @@ struct Sci_TextToFind { <td>Draws an overstrike caret as a block. This should be ored with one of the first three styles.</td> </tr> <tr> + <th align="left"><code>CARETSTYLE_CURSES</code></th> + <td>32</td> + <td>Draws carets that cannot be drawn in a curses (terminal) environment (such as additional carets), + and draws them as blocks. The main caret is left to be drawn by the terminal itself. This setting is + typically a standalone setting.</td> + </tr> + <tr> <th align="left"><code>CARETSTYLE_BLOCK_AFTER</code></th> <td>256</td> <td>When the caret end of a range is at the end and a block caret style is chosen, draws the block diff --git a/include/Scintilla.h b/include/Scintilla.h index a10c9ed82..b46e886b6 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -907,6 +907,7 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define CARETSTYLE_BLOCK 2 #define CARETSTYLE_OVERSTRIKE_BAR 0 #define CARETSTYLE_OVERSTRIKE_BLOCK 0x10 +#define CARETSTYLE_CURSES 0x20 #define CARETSTYLE_INS_MASK 0xF #define CARETSTYLE_BLOCK_AFTER 0x100 #define SCI_SETCARETSTYLE 2512 diff --git a/include/ScintillaTypes.h b/include/ScintillaTypes.h index 910cd5ec8..917878474 100644 --- a/include/ScintillaTypes.h +++ b/include/ScintillaTypes.h @@ -439,6 +439,7 @@ enum class CaretStyle { Block = 2, OverstrikeBar = 0, OverstrikeBlock = 0x10, + Curses = 0x20, InsMask = 0xF, BlockAfter = 0x100, }; diff --git a/src/EditView.cxx b/src/EditView.cxx index 3ab4c17a5..e0f52f501 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -1636,7 +1636,7 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt } const bool caretBlinkState = (model.caret.active && model.caret.on) || (!additionalCaretsBlink && !mainCaret); const bool caretVisibleState = additionalCaretsVisible || mainCaret; - if ((xposCaret >= 0) && vsDraw.IsCaretVisible() && + if ((xposCaret >= 0) && vsDraw.IsCaretVisible(mainCaret) && (drawDrag || (caretBlinkState && caretVisibleState))) { bool canDrawBlockCaret = true; bool drawBlockCaret = false; @@ -1660,7 +1660,8 @@ void EditView::DrawCarets(Surface *surface, const EditModel &model, const ViewSt if (xposCaret > 0) caretWidthOffset = 0.51f; // Move back so overlaps both character cells. xposCaret += xStart; - const ViewStyle::CaretShape caretShape = drawDrag ? ViewStyle::CaretShape::line : vsDraw.CaretShapeForMode(model.inOverstrike); + const ViewStyle::CaretShape caretShape = drawDrag ? ViewStyle::CaretShape::line : + vsDraw.CaretShapeForMode(model.inOverstrike, mainCaret); if (drawDrag) { /* Dragging text, use a line caret */ rcCaret.left = std::round(xposCaret - caretWidthOffset); @@ -1733,6 +1734,22 @@ static void DrawWrapIndentAndMarker(Surface *surface, const ViewStyle &vsDraw, c } } +// On the curses platform, the terminal is drawing its own caret, so if the +// caret is within the main selection, do not draw the selection at that +// position. +// Use iDoc from DrawBackground and DrawForeground here because PositionCache +// has broken up the line such that, if the caret is inside the main selection, +// the beginning or end of that selection is at the end of a text segment. +// This function should only be called if iDoc is within the main selection. +static InSelection characterInCursesSelection(Sci::Position iDoc, const EditModel &model, const ViewStyle &vsDraw) { + const SelectionPosition& posCaret = model.sel.RangeMain().caret; + const bool caretAtStart = posCaret < model.sel.RangeMain().anchor && posCaret.Position() == iDoc; + const bool caretAtEnd = posCaret > model.sel.RangeMain().anchor && + vsDraw.DrawCaretInsideSelection(false, false) && + model.pdoc->MovePositionOutsideChar(posCaret.Position()-1, -1) == iDoc; + return (caretAtStart || caretAtEnd) ? InSelection::inNone : InSelection::inMain; +} + void EditView::DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine, Range lineRange, Sci::Position posLineStart, int xStart, int subLine, std::optional<ColourRGBA> background) const { @@ -1743,7 +1760,7 @@ void EditView::DrawBackground(Surface *surface, const EditModel &model, const Vi // Does not take margin into account but not significant const XYPOSITION xStartVisible = static_cast<XYPOSITION>(subLineStart-xStart); - BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, nullptr); + BreakFinder bfBack(ll, &model.sel, lineRange, posLineStart, xStartVisible, selBackDrawn, model.pdoc, &model.reprs, &vsDraw); const bool drawWhitespaceBackground = vsDraw.WhitespaceBackgroundDrawn() && !background; @@ -1766,7 +1783,9 @@ void EditView::DrawBackground(Surface *surface, const EditModel &model, const Vi if (rcSegment.right > rcLine.right) rcSegment.right = rcLine.right; - const InSelection inSelection = hideSelection ? InSelection::inNone : model.sel.CharacterInSelection(iDoc); + InSelection inSelection = hideSelection ? InSelection::inNone : model.sel.CharacterInSelection(iDoc); + if (vsDraw.IsMainCursesCaret(inSelection == InSelection::inMain)) + inSelection = characterInCursesSelection(iDoc, model, vsDraw); const bool inHotspot = model.hotspot.Valid() && model.hotspot.ContainsCharacter(iDoc); ColourRGBA textBack = TextBackground(model, vsDraw, ll, background, inSelection, inHotspot, ll->styles[i], i); @@ -2010,7 +2029,9 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi } } } - const InSelection inSelection = hideSelection ? InSelection::inNone : model.sel.CharacterInSelection(iDoc); + InSelection inSelection = hideSelection ? InSelection::inNone : model.sel.CharacterInSelection(iDoc); + if (vsDraw.IsMainCursesCaret(inSelection == InSelection::inMain)) + inSelection = characterInCursesSelection(iDoc, model, vsDraw); const std::optional<ColourRGBA> selectionFore = SelectionForeground(model, vsDraw, inSelection); if (selectionFore) { textFore = *selectionFore; diff --git a/src/Editor.cxx b/src/Editor.cxx index 06d96134f..dc3cf6caf 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -7592,7 +7592,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { return vs.ElementColour(Element::Caret)->OpaqueRGB(); case Message::SetCaretStyle: - if (static_cast<CaretStyle>(wParam) <= (CaretStyle::Block | CaretStyle::OverstrikeBlock | CaretStyle::BlockAfter)) + if (static_cast<CaretStyle>(wParam) <= (CaretStyle::Block | CaretStyle::OverstrikeBlock | CaretStyle::Curses | CaretStyle::BlockAfter)) vs.caret.style = static_cast<CaretStyle>(wParam); else /* Default to the line caret */ diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 6eee26113..07507a7fa 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -690,10 +690,38 @@ BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lin for (size_t r=0; r<psel->Count(); r++) { const SelectionSegment portion = psel->Range(r).Intersect(segmentLine); if (!(portion.start == portion.end)) { - if (portion.start.IsValid()) - Insert(portion.start.Position() - posLineStart); - if (portion.end.IsValid()) - Insert(portion.end.Position() - posLineStart); + if (portion.start.IsValid()) { + const bool skipFirstSelectedCharacter = pvsDraw && pvsDraw->IsMainCursesCaret(r == 0) && + psel->Range(r).caret < psel->Range(r).anchor; + if (!skipFirstSelectedCharacter) { + Insert(portion.start.Position() - posLineStart); + } else { + // On the curses platform, the terminal is drawing its own caret, so + // make sure the main selection is not drawn on its first character + // if the caret is currently on it. + // While the caret is still inside the selection, it will be at the + // end of this text segment (instead of the beginning of the next + // one), so testing against this condition allows it to be drawn + // as not selected. + const Sci::Position next = pdoc->MovePositionOutsideChar(portion.start.Position()+1, 1); + Insert(next - posLineStart); + } + } + if (portion.end.IsValid()) { + const bool skipLastSelectedCharacter = pvsDraw && pvsDraw->IsMainCursesCaret(r == 0) && + psel->Range(r).caret > psel->Range(r).anchor && pvsDraw->DrawCaretInsideSelection(false, false); + if (!skipLastSelectedCharacter) { + Insert(portion.end.Position() - posLineStart); + } else { + // On the curses platform, the terminal is drawing its own caret, so + // make sure the main selection is not drawn on its last character + // if the caret is currently on it. The caret will be its own text + // segment and at the end of said segment, so testing against this + // condition allows it to be drawn as not selected. + const Sci::Position prev = pdoc->MovePositionOutsideChar(portion.end.Position()-1, -1); + Insert(prev - posLineStart); + } + } } } } diff --git a/src/ViewStyle.cxx b/src/ViewStyle.cxx index 5273841ed..92e2613bb 100644 --- a/src/ViewStyle.cxx +++ b/src/ViewStyle.cxx @@ -661,11 +661,18 @@ bool ViewStyle::SetWrapIndentMode(WrapIndentMode wrapIndentMode_) noexcept { bool ViewStyle::IsBlockCaretStyle() const noexcept { return ((caret.style & CaretStyle::InsMask) == CaretStyle::Block) || - FlagSet(caret.style, CaretStyle::OverstrikeBlock); + FlagSet(caret.style, CaretStyle::OverstrikeBlock) || + FlagSet(caret.style, CaretStyle::Curses); } -bool ViewStyle::IsCaretVisible() const noexcept { - return caret.width > 0 && caret.style != CaretStyle::Invisible; +bool ViewStyle::IsCaretVisible(bool isMainSelection) const noexcept { + return caret.width > 0 && + ((caret.style & CaretStyle::InsMask) != CaretStyle::Invisible || + (FlagSet(caret.style, CaretStyle::Curses) && !isMainSelection)); // only draw additional selections in curses mode +} + +bool ViewStyle::IsMainCursesCaret(bool isMainSelection) const noexcept { + return isMainSelection && FlagSet(caret.style, CaretStyle::Curses); } bool ViewStyle::DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOverride) const noexcept { @@ -673,14 +680,19 @@ bool ViewStyle::DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOv return false; return ((caret.style & CaretStyle::InsMask) == CaretStyle::Block) || (inOverstrike && FlagSet(caret.style, CaretStyle::OverstrikeBlock)) || - imeCaretBlockOverride; + imeCaretBlockOverride || + FlagSet(caret.style, CaretStyle::Curses); } -ViewStyle::CaretShape ViewStyle::CaretShapeForMode(bool inOverstrike) const noexcept { +ViewStyle::CaretShape ViewStyle::CaretShapeForMode(bool inOverstrike, bool isMainSelection) const noexcept { if (inOverstrike) { return (FlagSet(caret.style, CaretStyle::OverstrikeBlock)) ? CaretShape::block : CaretShape::bar; } + if (FlagSet(caret.style, CaretStyle::Curses) && !isMainSelection) { + return CaretShape::block; + } + const CaretStyle caretStyle = caret.style & CaretStyle::InsMask; return (caretStyle <= CaretStyle::Block) ? static_cast<CaretShape>(caretStyle) : CaretShape::line; } diff --git a/src/ViewStyle.h b/src/ViewStyle.h index de56c0895..704da635a 100644 --- a/src/ViewStyle.h +++ b/src/ViewStyle.h @@ -235,9 +235,10 @@ public: enum class CaretShape { invisible, line, block, bar }; bool IsBlockCaretStyle() const noexcept; - bool IsCaretVisible() const noexcept; + bool IsCaretVisible(bool isMainSelection) const noexcept; + bool IsMainCursesCaret(bool isMainSelection) const noexcept; bool DrawCaretInsideSelection(bool inOverstrike, bool imeCaretBlockOverride) const noexcept; - CaretShape CaretShapeForMode(bool inOverstrike) const noexcept; + CaretShape CaretShapeForMode(bool inOverstrike, bool isMainSelection) const noexcept; private: void AllocStyles(size_t sizeNew); |