aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorNeil <nyamatongwe@gmail.com>2021-07-31 08:49:25 +1000
committerNeil <nyamatongwe@gmail.com>2021-07-31 08:49:25 +1000
commit4d1a31d6a74019c3d43de1c33e2be093ed6dfd11 (patch)
treed2b113b25d2cfcb751f4b5a961bb14a04261175f
parent297d17ace8501b23a32390163676c838dad71f9a (diff)
downloadscintilla-mirror-4d1a31d6a74019c3d43de1c33e2be093ed6dfd11.tar.gz
Implement StyleSetCheckMonospaced.
-rw-r--r--call/ScintillaCall.cxx8
-rw-r--r--doc/ScintillaDoc.html25
-rw-r--r--include/Scintilla.h2
-rw-r--r--include/Scintilla.iface6
-rw-r--r--include/ScintillaCall.h2
-rw-r--r--include/ScintillaMessages.h2
-rw-r--r--src/Editor.cxx9
-rw-r--r--src/PositionCache.cxx16
-rw-r--r--src/Style.cxx1
-rw-r--r--src/Style.h2
-rw-r--r--src/ViewStyle.cxx19
-rw-r--r--test/simpleTests.py5
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) &rarr; 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) &rarr; bool</a><br />
<a class="message" href="#SCI_SETFONTLOCALE">SCI_SETFONTLOCALE(&lt;unused&gt;, const char *localeName)</a><br />
<a class="message" href="#SCI_GETFONTLOCALE">SCI_GETFONTLOCALE(&lt;unused&gt;, char *localeName) &rarr; 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) &rarr; bool</b><br />
+ This attribute indicates that the font may be monospaced over the ASCII graphics characters (' ' &hellip; '~',
+ including letters ('a'&hellip;'z', 'A'&hellip;'Z') and numbers ('0'&hellip;'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 '&#x1F603;', Arabic characters '&#x0634;' and Chinese ideographs '&#x6F22;' 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 ' ' &hellip; '~' 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(&lt;unused&gt;, const char *localeName)</b><br />
<b id="SCI_GETFONTLOCALE">SCI_GETFONTLOCALE(&lt;unused&gt;, char *localeName NUL-terminated) &rarr; 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.