diff options
| author | Neil <nyamatongwe@gmail.com> | 2021-07-31 08:49:25 +1000 | 
|---|---|---|
| committer | Neil <nyamatongwe@gmail.com> | 2021-07-31 08:49:25 +1000 | 
| commit | 4d1a31d6a74019c3d43de1c33e2be093ed6dfd11 (patch) | |
| tree | d2b113b25d2cfcb751f4b5a961bb14a04261175f | |
| parent | 297d17ace8501b23a32390163676c838dad71f9a (diff) | |
| download | scintilla-mirror-4d1a31d6a74019c3d43de1c33e2be093ed6dfd11.tar.gz | |
Implement StyleSetCheckMonospaced.
| -rw-r--r-- | call/ScintillaCall.cxx | 8 | ||||
| -rw-r--r-- | doc/ScintillaDoc.html | 25 | ||||
| -rw-r--r-- | include/Scintilla.h | 2 | ||||
| -rw-r--r-- | include/Scintilla.iface | 6 | ||||
| -rw-r--r-- | include/ScintillaCall.h | 2 | ||||
| -rw-r--r-- | include/ScintillaMessages.h | 2 | ||||
| -rw-r--r-- | src/Editor.cxx | 9 | ||||
| -rw-r--r-- | src/PositionCache.cxx | 16 | ||||
| -rw-r--r-- | src/Style.cxx | 1 | ||||
| -rw-r--r-- | src/Style.h | 2 | ||||
| -rw-r--r-- | src/ViewStyle.cxx | 19 | ||||
| -rw-r--r-- | test/simpleTests.py | 5 | 
12 files changed, 96 insertions, 1 deletions
| diff --git a/call/ScintillaCall.cxx b/call/ScintillaCall.cxx index 23de3f004..380d73494 100644 --- a/call/ScintillaCall.cxx +++ b/call/ScintillaCall.cxx @@ -624,6 +624,14 @@ void ScintillaCall::StyleSetHotSpot(int style, bool hotspot) {  	Call(Message::StyleSetHotSpot, style, hotspot);  } +void ScintillaCall::StyleSetCheckMonospaced(int style, bool checkMonospaced) { +	Call(Message::StyleSetCheckMonospaced, style, checkMonospaced); +} + +bool ScintillaCall::StyleGetCheckMonospaced(int style) { +	return Call(Message::StyleGetCheckMonospaced, 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 eccf3ee55..245b467c4 100644 --- a/doc/ScintillaDoc.html +++ b/doc/ScintillaDoc.html @@ -3021,6 +3021,9 @@ struct Sci_TextToFind {       <a class="message" href="#SCI_STYLESETHOTSPOT">SCI_STYLESETHOTSPOT(int style, bool      hotspot)</a><br />      <a class="message" href="#SCI_STYLEGETHOTSPOT">SCI_STYLEGETHOTSPOT(int style) → bool</a><br /> +     <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_SETFONTLOCALE">SCI_SETFONTLOCALE(<unused>, const char *localeName)</a><br />      <a class="message" href="#SCI_GETFONTLOCALE">SCI_GETFONTLOCALE(<unused>, char *localeName) → int</a><br />      </code> @@ -3265,6 +3268,28 @@ struct Sci_TextToFind {      may change and an underline appear to indicate that these areas are sensitive to clicking.      This may be used to allow hyperlinks to other documents.</p> +    <p><b id="SCI_STYLESETCHECKMONOSPACED">SCI_STYLESETCHECKMONOSPACED(int style, bool +    checkMonospaced)</b><br /> +    <b id="SCI_STYLEGETCHECKMONOSPACED">SCI_STYLEGETCHECKMONOSPACED(int style) → bool</b><br /> +    This attribute indicates that the font may be monospaced over the ASCII graphics characters (' ' … '~', +    including letters ('a'…'z', 'A'…'Z') and numbers ('0'…'9')). +    This allows optimizing speed and memory use for some common scenarios where documents are mostly composed from ASCII +    characters.</p> +    <p> +    Fonts are rarely monospaced over all possible characters. +    Emoji '😃', Arabic characters 'ش' and Chinese ideographs '漢' are often different widths to Roman letters. +    Even when a font is designed as monospaced, not all characters may be present +    and platforms may substitute other fonts for any missing characters with the substitute characters taking more or less space. +    However, fonts are often reliably monospaced over ASCII text (disregarding control characters) and many documents contain +    mostly ASCII characters. +    This setting allows simplified position calculations for text runs that are purely ASCII graphics characters. +    </p> +    <p> +    Before treating the font as monospaced, it is first checked over the ' ' … '~' range and for some known combinations of characters +    that may have different spacings because of kerning or ligatures. +    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_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/include/Scintilla.h b/include/Scintilla.h index 3a6e301b3..d08d90e80 100644 --- a/include/Scintilla.h +++ b/include/Scintilla.h @@ -272,6 +272,8 @@ typedef sptr_t (*SciFnDirectStatus)(sptr_t ptr, unsigned int iMessage, uptr_t wP  #define SCI_STYLEGETWEIGHT 2064  #define SCI_STYLESETCHARACTERSET 2066  #define SCI_STYLESETHOTSPOT 2409 +#define SCI_STYLESETCHECKMONOSPACED 2254 +#define SCI_STYLEGETCHECKMONOSPACED 2255  #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 837e4d841..c83773803 100644 --- a/include/Scintilla.iface +++ b/include/Scintilla.iface @@ -677,6 +677,12 @@ set void StyleSetCharacterSet=2066(int style, CharacterSet characterSet)  # Set a style to be a hotspot or not.  set void StyleSetHotSpot=2409(int style, bool hotspot) +# Indicate that a style may be monospaced over ASCII graphics characters which enables optimizations. +set void StyleSetCheckMonospaced=2254(int style, bool checkMonospaced) + +# Get whether a style may be monospaced. +get bool StyleGetCheckMonospaced=2255(int style,) +  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 098b9ec1d..d65dbc49f 100644 --- a/include/ScintillaCall.h +++ b/include/ScintillaCall.h @@ -196,6 +196,8 @@ public:  	Scintilla::FontWeight StyleGetWeight(int style);  	void StyleSetCharacterSet(int style, Scintilla::CharacterSet characterSet);  	void StyleSetHotSpot(int style, bool hotspot); +	void StyleSetCheckMonospaced(int style, bool checkMonospaced); +	bool StyleGetCheckMonospaced(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 9293a5a6b..48fbaff31 100644 --- a/include/ScintillaMessages.h +++ b/include/ScintillaMessages.h @@ -133,6 +133,8 @@ enum class Message {  	StyleGetWeight = 2064,  	StyleSetCharacterSet = 2066,  	StyleSetHotSpot = 2409, +	StyleSetCheckMonospaced = 2254, +	StyleGetCheckMonospaced = 2255,  	SetElementColour = 2753,  	GetElementColour = 2754,  	ResetElementColour = 2755, diff --git a/src/Editor.cxx b/src/Editor.cxx index db7e2366e..bd2742539 100644 --- a/src/Editor.cxx +++ b/src/Editor.cxx @@ -117,7 +117,7 @@ static constexpr bool IsAllSpacesOrTabs(std::string_view sv) noexcept {  	return true;  } -Editor::Editor() : durationWrapOneByte(0.000001, 0.0000001, 0.00001) { +Editor::Editor() : durationWrapOneByte(0.000001, 0.00000001, 0.00001) {  	ctrlID = 0;  	stylesValid = false; @@ -5748,6 +5748,9 @@ void Editor::StyleSetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {  	case Message::StyleSetHotSpot:  		vs.styles[wParam].hotspot = lParam != 0;  		break; +	case Message::StyleSetCheckMonospaced: +		vs.styles[wParam].checkMonospaced = lParam != 0; +		break;  	default:  		break;  	} @@ -5787,6 +5790,8 @@ sptr_t Editor::StyleGetMessage(Message iMessage, uptr_t wParam, sptr_t lParam) {  		return vs.styles[wParam].changeable ? 1 : 0;  	case Message::StyleGetHotSpot:  		return vs.styles[wParam].hotspot ? 1 : 0; +	case Message::StyleGetCheckMonospaced: +		return vs.styles[wParam].checkMonospaced ? 1 : 0;  	default:  		break;  	} @@ -7202,6 +7207,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {  	case Message::StyleSetVisible:  	case Message::StyleSetChangeable:  	case Message::StyleSetHotSpot: +	case Message::StyleSetCheckMonospaced:  		StyleSetMessage(iMessage, wParam, lParam);  		break; @@ -7220,6 +7226,7 @@ sptr_t Editor::WndProc(Message iMessage, uptr_t wParam, sptr_t lParam) {  	case Message::StyleGetVisible:  	case Message::StyleGetChangeable:  	case Message::StyleGetHotSpot: +	case Message::StyleGetCheckMonospaced:  		return StyleGetMessage(iMessage, wParam, lParam);  	case Message::StyleResetDefault: diff --git a/src/PositionCache.cxx b/src/PositionCache.cxx index 714937dc1..fda736cc7 100644 --- a/src/PositionCache.cxx +++ b/src/PositionCache.cxx @@ -377,6 +377,14 @@ constexpr size_t AlignUp(size_t value, size_t alignment) noexcept {  constexpr size_t alignmentLLC = 20; +constexpr bool GraphicASCII(char ch) noexcept { +	return ch >= ' ' && ch <= '~'; +} + +bool AllGraphicASCII(std::string_view text) noexcept { +	return std::all_of(text.cbegin(), text.cend(), GraphicASCII); +} +  } @@ -895,6 +903,14 @@ void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, uns  			probe = probe2;  		}  	} +	if (vstyle.styles[styleNumber].monospaceASCII) { +		if (AllGraphicASCII(sv)) { +			for (size_t i = 0; i < sv.length(); i++) { +				positions[i] = vstyle.styles[styleNumber].aveCharWidth * (i+1); +			} +			return; +		} +	}  	const Font *fontStyle = vstyle.styles[styleNumber].font.get();  	surface->MeasureWidths(fontStyle, sv, positions);  	if (probe < pces.size()) { diff --git a/src/Style.cxx b/src/Style.cxx index d71b62d97..3a57d1a08 100644 --- a/src/Style.cxx +++ b/src/Style.cxx @@ -57,6 +57,7 @@ void FontMeasurements::ClearMeasurements() noexcept {  	capitalHeight = 1;  	aveCharWidth = 1;  	spaceWidth = 1; +	monospaceASCII = false;  	sizeZoomed = 2;  } diff --git a/src/Style.h b/src/Style.h index 8153d156e..087b13b2f 100644 --- a/src/Style.h +++ b/src/Style.h @@ -17,6 +17,7 @@ struct FontSpecification {  	int size;  	Scintilla::CharacterSet characterSet;  	Scintilla::FontQuality extraFontFlag; +	bool checkMonospaced = false;  	FontSpecification() noexcept :  		fontName(nullptr),  		weight(Scintilla::FontWeight::Normal), @@ -35,6 +36,7 @@ struct FontMeasurements {  	XYPOSITION capitalHeight;	// Top of capital letter to baseline: ascent - internal leading  	XYPOSITION aveCharWidth;  	XYPOSITION spaceWidth; +	bool monospaceASCII = false;  	int sizeZoomed;  	FontMeasurements() noexcept;  	void ClearMeasurements() noexcept; diff --git a/src/ViewStyle.cxx b/src/ViewStyle.cxx index 33b861445..47f76d357 100644 --- a/src/ViewStyle.cxx +++ b/src/ViewStyle.cxx @@ -18,6 +18,7 @@  #include <optional>  #include <algorithm>  #include <memory> +#include <numeric>  #include "ScintillaTypes.h" @@ -64,6 +65,24 @@ void FontRealised::Realise(Surface &surface, int zoomLevel, Technology technolog  	capitalHeight = surface.Ascent(font.get()) - surface.InternalLeading(font.get());  	aveCharWidth = surface.AverageCharWidth(font.get());  	spaceWidth = surface.WidthText(font.get(), " "); + +	if (fs.checkMonospaced) { +		std::string allASCIIGraphic("Ayfi");	// "Ay" is normally strongly kerned and "fi" may be a ligature +		for (unsigned char ch = 0x20; ch <= 0x7E; ch++) { +			allASCIIGraphic.push_back(ch); +		} +		std::vector<XYPOSITION> positions(allASCIIGraphic.length()); +		surface.MeasureWidths(font.get(), allASCIIGraphic, positions.data()); +		std::adjacent_difference(positions.begin(), positions.end(), positions.begin()); +		const XYPOSITION maxWidth = *std::max_element(positions.begin(), positions.end()); +		const XYPOSITION minWidth = *std::min_element(positions.begin(), positions.end()); +		const XYPOSITION variance = maxWidth - minWidth; +		const XYPOSITION scaledVariance = variance / aveCharWidth; +		constexpr XYPOSITION monospaceWidthEpsilon = 0.000001;	// May need tweaking if monospace fonts vary more +		monospaceASCII = scaledVariance < monospaceWidthEpsilon; +	} else { +		monospaceASCII = false; +	}  }  ViewStyle::ViewStyle() : markers(MarkerMax + 1), indicators(static_cast<size_t>(IndicatorNumbers::Max) + 1) { diff --git a/test/simpleTests.py b/test/simpleTests.py index dca1e24c4..e8b9e69c4 100644 --- a/test/simpleTests.py +++ b/test/simpleTests.py @@ -1985,6 +1985,11 @@ class TestStyleAttributes(unittest.TestCase):  		self.ed.FontLocale = testLocale  		self.assertEquals(self.ed.GetFontLocale(), testLocale) +	def testCheckMonospaced(self): +		self.assertEquals(self.ed.StyleGetCheckMonospaced(self.ed.STYLE_DEFAULT), 0) +		self.ed.StyleSetCheckMonospaced(self.ed.STYLE_DEFAULT, 1) +		self.assertEquals(self.ed.StyleGetCheckMonospaced(self.ed.STYLE_DEFAULT), 1) +  class TestElements(unittest.TestCase):  	""" These tests are just to ensure that the calls set and retrieve values.  	They do not check the visual appearance of the style attributes. | 
