diff options
| -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) | 
