diff options
author | Ferdinand Oeinck <unknown> | 2022-10-04 09:35:06 +1100 |
---|---|---|
committer | Ferdinand Oeinck <unknown> | 2022-10-04 09:35:06 +1100 |
commit | f4d5c00424eff7c84b4152b4df4de5c0ba4b12bc (patch) | |
tree | e239ae33e6bfb8844a150bb1c25b1b643741211d | |
parent | 232fad28e7003fd38e2468258bcfacc61381ef7a (diff) | |
download | scintilla-mirror-f4d5c00424eff7c84b4152b4df4de5c0ba4b12bc.tar.gz |
Feature [feature-requests:#1453] Added SCI_STYLESETINVISIBLEREPRESENTATION to
make it easier to edit around invisible text.
This also allows representing long lexemes with a single character to provide a
summarized view.
-rw-r--r-- | call/ScintillaCall.cxx | 12 | ||||
-rw-r--r-- | doc/ScintillaDoc.html | 11 | ||||
-rw-r--r-- | doc/ScintillaHistory.html | 6 | ||||
-rw-r--r-- | include/Scintilla.h | 2 | ||||
-rw-r--r-- | include/Scintilla.iface | 6 | ||||
-rw-r--r-- | include/ScintillaCall.h | 3 | ||||
-rw-r--r-- | include/ScintillaMessages.h | 2 | ||||
-rw-r--r-- | src/EditView.cxx | 24 | ||||
-rw-r--r-- | src/Editor.cxx | 17 | ||||
-rw-r--r-- | src/Style.cxx | 3 | ||||
-rw-r--r-- | src/Style.h | 1 | ||||
-rw-r--r-- | test/simpleTests.py | 8 |
12 files changed, 94 insertions, 1 deletions
diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx index 1f496c53d..434e3fdf0 100644 --- a/call/ScintillaCall.cxx +++ b/call/ScintillaCall.cxx @@ -647,6 +647,18 @@ bool ScintillaCall::StyleGetCheckMonospaced(int style) { return Call(Message::StyleGetCheckMonospaced, style); } +void ScintillaCall::StyleSetInvisibleRepresentation(int style, const char *representation) { + CallString(Message::StyleSetInvisibleRepresentation, style, representation); +} + +int ScintillaCall::StyleGetInvisibleRepresentation(int style, char *representation) { + return static_cast<int>(CallPointer(Message::StyleGetInvisibleRepresentation, style, representation)); +} + +std::string ScintillaCall::StyleGetInvisibleRepresentation(int style) { + return CallReturnString(Message::StyleGetInvisibleRepresentation, style); +} + void ScintillaCall::SetElementColour(Scintilla::Element element, ColourAlpha colourElement) { Call(Message::SetElementColour, static_cast<uintptr_t>(element), colourElement); } diff --git a/doc/ScintillaDoc.html b/doc/ScintillaDoc.html index a9a7e72ed..fb5d59dfd 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -3248,6 +3248,8 @@ struct Sci_TextToFindFull { <a class="message" href="#SCI_STYLESETCHECKMONOSPACED">SCI_STYLESETCHECKMONOSPACED(int style, bool checkMonospaced)</a><br /> <a class="message" href="#SCI_STYLEGETCHECKMONOSPACED">SCI_STYLEGETCHECKMONOSPACED(int style) → bool</a><br /> + <a class="message" href="#SCI_STYLESETINVISIBLEREPRESENTATION">SCI_STYLESETINVISIBLEREPRESENTATION(int style, const char *representation)</a><br /> + <a class="message" href="#SCI_STYLEGETINVISIBLEREPRESENTATION">SCI_STYLEGETINVISIBLEREPRESENTATION(int style, char *representation NUL-terminated) → int</a><br /> <a class="message" href="#SCI_SETFONTLOCALE">SCI_SETFONTLOCALE(<unused>, const char *localeName)</a><br /> <a class="message" href="#SCI_GETFONTLOCALE">SCI_GETFONTLOCALE(<unused>, char *localeName) → int</a><br /> </code> @@ -3514,6 +3516,15 @@ struct Sci_TextToFindFull { Applications may apply the 'check monospaced' attribute just to fonts known to be monospaced or on all fonts, leaving it to Scintilla to reject fonts that are proportional.</p> + <p><b id="SCI_STYLESETINVISIBLEREPRESENTATION">SCI_STYLESETINVISIBLEREPRESENTATION(int style, const char *representation)</b><br /> + <b id="SCI_STYLEGETINVISIBLEREPRESENTATION">SCI_STYLEGETINVISIBLEREPRESENTATION(int style, char *representation NUL-terminated) → int</b><br /> + When a style is made invisible with <a class="seealso" href="#SCI_STYLESETVISIBLE">SCI_STYLESETVISIBLE</a>, text is difficult to edit as + the cursor can be at both sides of the invisible text segment. With these messages invisible text segements can be made visible with a single + UTF8 characater giving the user an indication if the cursor is left or right of the invisible text. The character is displayed using the current style.</p> + + <p>The <code>representation</code> parameter is a zero terminated string holding the one character used to represent the invisible text segment. Only the first character + is used, the character is decoded as UTF-8.</p> + <p><b id="SCI_SETFONTLOCALE">SCI_SETFONTLOCALE(<unused>, const char *localeName)</b><br /> <b id="SCI_GETFONTLOCALE">SCI_GETFONTLOCALE(<unused>, char *localeName NUL-terminated) → int</b><br /> These messages set the locale used for font selection with language-dependent glyphs. diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index abbf4f4ef..cac690226 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -573,6 +573,7 @@ <td>Jacky Yang</td> </tr><tr> <td>Reinhard Nißl</td> + <td>Ferdinand Oeinck</td> </tr> </table> <h2>Releases</h2> @@ -584,6 +585,11 @@ Released 27 August 2022. </li> <li> + Added SCI_STYLESETINVISIBLEREPRESENTATION to make it easier to edit around invisible text. + This also allows representing long lexemes with a single character to provide a summarized view. + <a href="https://sourceforge.net/p/scintilla/feature-requests/1453/">Feature #1453</a>. + </li> + <li> Removed NotifyLexerChanged notification from DocWatcher. This is a private interface but could be used by independent platform layers and was exposed by ScintillaDocument in the Qt implementation of ScintillaEdit. diff --git a/include/Scintilla.h b/include/Scintilla.h index 5781674e3..03302e293 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -280,6 +280,8 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP #define SCI_STYLESETHOTSPOT 2409 #define SCI_STYLESETCHECKMONOSPACED 2254 #define SCI_STYLEGETCHECKMONOSPACED 2255 +#define SCI_STYLESETINVISIBLEREPRESENTATION 2256 +#define SCI_STYLEGETINVISIBLEREPRESENTATION 2257 #define SC_ELEMENT_LIST 0 #define SC_ELEMENT_LIST_BACK 1 #define SC_ELEMENT_LIST_SELECTED 2 diff --git a/include/Scintilla.iface b/include/Scintilla.iface index bd6cb76de..b03b8c8ca 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -694,6 +694,12 @@ set void StyleSetCheckMonospaced=2254(int style, bool checkMonospaced) # Get whether a style may be monospaced. get bool StyleGetCheckMonospaced=2255(int style,) +# Set the invisible representation for a style. +set void StyleSetInvisibleRepresentation=2256(int style, string representation) + +# Get the invisible representation for a style. +get int StyleGetInvisibleRepresentation=2257(int style, stringresult representation) + enu Element=SC_ELEMENT_ val SC_ELEMENT_LIST=0 val SC_ELEMENT_LIST_BACK=1 diff --git a/include/ScintillaCall.h b/include/ScintillaCall.h index 0e79e37db..4b8e3c732 100644 --- a/include/ScintillaCall.h +++ b/include/ScintillaCall.h @@ -200,6 +200,9 @@ public: void StyleSetHotSpot(int style, bool hotspot); void StyleSetCheckMonospaced(int style, bool checkMonospaced); bool StyleGetCheckMonospaced(int style); + void StyleSetInvisibleRepresentation(int style, const char *representation); + int StyleGetInvisibleRepresentation(int style, char *representation); + std::string StyleGetInvisibleRepresentation(int style); void SetElementColour(Scintilla::Element element, ColourAlpha colourElement); ColourAlpha ElementColour(Scintilla::Element element); void ResetElementColour(Scintilla::Element element); diff --git a/include/ScintillaMessages.h b/include/ScintillaMessages.h index 663d6e12c..118655689 100644 --- a/include/ScintillaMessages.h +++ b/include/ScintillaMessages.h @@ -136,6 +136,8 @@ enum class Message { StyleSetHotSpot = 2409, StyleSetCheckMonospaced = 2254, StyleGetCheckMonospaced = 2255, + StyleSetInvisibleRepresentation = 2256, + StyleGetInvisibleRepresentation = 2257, SetElementColour = 2753, GetElementColour = 2754, ResetElementColour = 2755, diff --git a/src/EditView.cxx b/src/EditView.cxx index 86e59b4dc..8a7a50a7e 100644 --- a/src/EditView.cxx +++ b/src/EditView.cxx @@ -435,6 +435,21 @@ void LayoutSegments(IPositionCache *pCache, std::string_view(&ll->chars[ts.start], ts.length), &ll->positions[ts.start + 1], multiThreaded); } } + } else if (vstyle.styles[ll->styles[ts.start]].invisibleRepresentation[0]) { + const int styleInvisible = ll->styles[ts.start]; + const std::string_view text = vstyle.styles[styleInvisible].invisibleRepresentation; + XYPOSITION positionsRepr[Representation::maxLength + 1]; + // invisibleRepresentation is UTF-8 which only matches cache if document is UTF-8 + // or it only contains ASCII which is a subset of all currently supported encodings. + if (textUnicode || ViewIsASCII(text)) { + pCache->MeasureWidths(surface, vstyle, styleInvisible, text, positionsRepr, multiThreaded); + } else { + surface->MeasureWidthsUTF8(vstyle.styles[styleInvisible].font.get(), text, positionsRepr); + } + const XYPOSITION representationWidth = positionsRepr[text.length() - 1]; + for (int ii = 0; ii < ts.length; ii++) { + ll->positions[ts.start + 1 + ii] = representationWidth; + } } } } @@ -2230,6 +2245,15 @@ void EditView::DrawForeground(Surface *surface, const EditModel &model, const Vi surface->DrawTextNoClip(rcSegment, textFont, rcSegment.top + vsDraw.maxAscent, text, textFore, textBack); } + } else if (vsDraw.styles[styleMain].invisibleRepresentation[0]) { + const std::string_view text = vsDraw.styles[styleMain].invisibleRepresentation; + if (phasesDraw != PhasesDraw::One) { + surface->DrawTextTransparentUTF8(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, text, textFore); + } else { + surface->DrawTextNoClipUTF8(rcSegment, textFont, + rcSegment.top + vsDraw.maxAscent, text, textFore, textBack); + } } if (vsDraw.viewWhitespace != WhiteSpace::Invisible || (inIndentation && vsDraw.viewIndentationGuides != IndentView::None)) { diff --git a/src/Editor.cxx b/src/Editor.cxx index 97c141bae..35148299e 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -5832,6 +5832,19 @@ void Editor::StyleSetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::StyleSetVisible: vs.styles[wParam].visible = lParam != 0; break; + case Message::StyleSetInvisibleRepresentation: { + const char *utf8 = ConstCharPtrFromSPtr(lParam); + char *rep = vs.styles[wParam].invisibleRepresentation; + const int classified = UTF8Classify(utf8); + if (!(classified & UTF8MaskInvalid)) { + // valid UTF-8 + int len = classified & UTF8MaskWidth; + while (len--) + *rep++ = *utf8++; + } + *rep = 0; + break; + } case Message::StyleSetChangeable: vs.styles[wParam].changeable = lParam != 0; break; @@ -5878,6 +5891,8 @@ sptr_t Editor::StyleGetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) { return vs.styles[wParam].visible ? 1 : 0; case Message::StyleGetChangeable: return vs.styles[wParam].changeable ? 1 : 0; + case Message::StyleGetInvisibleRepresentation: + return StringResult(lParam, vs.styles[wParam].invisibleRepresentation); case Message::StyleGetHotSpot: return vs.styles[wParam].hotspot ? 1 : 0; case Message::StyleGetCheckMonospaced: @@ -7328,6 +7343,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::StyleSetChangeable: case Message::StyleSetHotSpot: case Message::StyleSetCheckMonospaced: + case Message::StyleSetInvisibleRepresentation: StyleSetMessage(iMessage, wParam, lParam); break; @@ -7347,6 +7363,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) { case Message::StyleGetChangeable: case Message::StyleGetHotSpot: case Message::StyleGetCheckMonospaced: + case Message::StyleGetInvisibleRepresentation: return StyleGetMessage(iMessage, wParam, lParam); case Message::StyleResetDefault: diff --git a/src/Style.cxx b/src/Style.cxx index 76ffa4942..a45072292 100644 --- a/src/Style.cxx +++ b/src/Style.cxx @@ -72,7 +72,8 @@ Style::Style(const char *fontName_) noexcept : caseForce(CaseForce::mixed), visible(true), changeable(true), - hotspot(false) { + hotspot(false), + invisibleRepresentation{} { } void Style::Copy(std::shared_ptr<Font> font_, const FontMeasurements &fm_) noexcept { diff --git a/src/Style.h b/src/Style.h index 6cef6bb60..da8159d6c 100644 --- a/src/Style.h +++ b/src/Style.h @@ -51,6 +51,7 @@ public: bool visible; bool changeable; bool hotspot; + char invisibleRepresentation[5]; std::shared_ptr<Font> font; diff --git a/test/simpleTests.py b/test/simpleTests.py index 6c55c7cf4..92d6a92a7 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -1956,6 +1956,7 @@ class TestStyleAttributes(unittest.TestCase): self.ed.EmptyUndoBuffer() self.testColour = 0x171615 self.testFont = b"Georgia" + self.testRepresentation = "\N{BULLET}".encode("utf-8") def tearDown(self): self.ed.StyleResetDefault() @@ -1971,6 +1972,13 @@ class TestStyleAttributes(unittest.TestCase): self.ed.StyleSetSizeFractional(self.ed.STYLE_DEFAULT, 1234) self.assertEquals(self.ed.StyleGetSizeFractional(self.ed.STYLE_DEFAULT), 1234) + def testInvisibleRepresentation(self): + self.assertEquals(self.ed.StyleGetInvisibleRepresentation(self.ed.STYLE_DEFAULT), b"") + self.ed.StyleSetInvisibleRepresentation(self.ed.STYLE_DEFAULT, self.testRepresentation) + self.assertEquals(self.ed.StyleGetInvisibleRepresentation(self.ed.STYLE_DEFAULT), self.testRepresentation) + self.ed.StyleSetInvisibleRepresentation(self.ed.STYLE_DEFAULT, b"\000") + self.assertEquals(self.ed.StyleGetInvisibleRepresentation(self.ed.STYLE_DEFAULT), b"") + def testBold(self): self.ed.StyleSetBold(self.ed.STYLE_DEFAULT, 1) self.assertEquals(self.ed.StyleGetBold(self.ed.STYLE_DEFAULT), 1) |