1 // Scintilla source code edit control
2 /** @file ViewStyle.cxx
3 ** Store information on how the document is to be viewed.
5 // Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
15 #include <string_view>
25 #include "ScintillaTypes.h"
27 #include "Debugging.h"
32 #include "UniqueString.h"
33 #include "Indicator.h"
35 #include "LineMarker.h"
37 #include "ViewStyle.h"
39 using namespace Scintilla
;
40 using namespace Scintilla::Internal
;
42 MarginStyle::MarginStyle(MarginType style_
, int width_
, int mask_
) noexcept
:
43 style(style_
), width(width_
), mask(mask_
), sensitive(false), cursor(CursorShape::ReverseArrow
) {
46 bool MarginStyle::ShowsFolding() const noexcept
{
47 return (mask
& MaskFolders
) != 0;
50 void FontRealised::Realise(Surface
&surface
, int zoomLevel
, Technology technology
, const FontSpecification
&fs
, const char *localeName
) {
51 PLATFORM_ASSERT(fs
.fontName
);
52 measurements
.sizeZoomed
= fs
.size
+ zoomLevel
* FontSizeMultiplier
;
53 if (measurements
.sizeZoomed
<= FontSizeMultiplier
) // May fail if sizeZoomed < 1
54 measurements
.sizeZoomed
= FontSizeMultiplier
;
56 const float deviceHeight
= static_cast<float>(surface
.DeviceHeightFont(measurements
.sizeZoomed
));
57 const FontParameters
fp(fs
.fontName
, deviceHeight
/ FontSizeMultiplier
, fs
.weight
,
58 fs
.italic
, fs
.extraFontFlag
, technology
, fs
.characterSet
, localeName
);
59 font
= Font::Allocate(fp
);
61 // floor here is historical as platform layers have tweaked their values to match.
62 // ceil would likely be better to ensure (nearly) all of the ink of a character is seen
63 // but that would require platform layer changes.
64 measurements
.ascent
= std::floor(surface
.Ascent(font
.get()));
65 measurements
.descent
= std::floor(surface
.Descent(font
.get()));
67 measurements
.capitalHeight
= surface
.Ascent(font
.get()) - surface
.InternalLeading(font
.get());
68 measurements
.aveCharWidth
= surface
.AverageCharWidth(font
.get());
69 measurements
.monospaceCharacterWidth
= measurements
.aveCharWidth
;
70 measurements
.spaceWidth
= surface
.WidthText(font
.get(), " ");
72 if (fs
.checkMonospaced
) {
73 // "Ay" is normally strongly kerned and "fi" may be a ligature
74 constexpr std::string_view
allASCIIGraphic("Ayfi"
75 // python: ''.join(chr(ch) for ch in range(32, 127))
76 " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
77 std::array
<XYPOSITION
, allASCIIGraphic
.length()> positions
{};
78 surface
.MeasureWidthsUTF8(font
.get(), allASCIIGraphic
, positions
.data());
79 std::adjacent_difference(positions
.begin(), positions
.end(), positions
.begin());
80 const XYPOSITION maxWidth
= *std::max_element(positions
.begin(), positions
.end());
81 const XYPOSITION minWidth
= *std::min_element(positions
.begin(), positions
.end());
82 const XYPOSITION variance
= maxWidth
- minWidth
;
83 const XYPOSITION scaledVariance
= variance
/ measurements
.aveCharWidth
;
84 constexpr XYPOSITION monospaceWidthEpsilon
= 0.000001; // May need tweaking if monospace fonts vary more
85 measurements
.monospaceASCII
= scaledVariance
< monospaceWidthEpsilon
;
86 measurements
.monospaceCharacterWidth
= minWidth
;
88 measurements
.monospaceASCII
= false;
92 ViewStyle::ViewStyle(size_t stylesSize_
) :
94 markers(MarkerMax
+ 1),
95 indicators(static_cast<size_t>(IndicatorNumbers::Max
) + 1) {
97 nextExtendedStyle
= 256;
100 // There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
101 largestMarkerHeight
= 0;
103 indicators
[0] = Indicator(IndicatorStyle::Squiggle
, ColourRGBA(0, 0x7f, 0));
104 indicators
[1] = Indicator(IndicatorStyle::TT
, ColourRGBA(0, 0, 0xff));
105 indicators
[2] = Indicator(IndicatorStyle::Plain
, ColourRGBA(0xff, 0, 0));
107 technology
= Technology::Default
;
108 indicatorsDynamic
= false;
109 indicatorsSetFore
= false;
116 tabWidth
= spaceWidth
* 8;
118 // Default is for no selection foregrounds
119 elementColours
.erase(Element::SelectionText
);
120 elementColours
.erase(Element::SelectionAdditionalText
);
121 elementColours
.erase(Element::SelectionSecondaryText
);
122 elementColours
.erase(Element::SelectionInactiveText
);
123 // Shades of grey for selection backgrounds
124 elementBaseColours
[Element::SelectionBack
] = ColourRGBA(0xc0, 0xc0, 0xc0, 0xff);
125 elementBaseColours
[Element::SelectionAdditionalBack
] = ColourRGBA(0xd7, 0xd7, 0xd7, 0xff);
126 elementBaseColours
[Element::SelectionSecondaryBack
] = ColourRGBA(0xb0, 0xb0, 0xb0, 0xff);
127 elementBaseColours
[Element::SelectionInactiveBack
] = ColourRGBA(0x80, 0x80, 0x80, 0x3f);
128 elementAllowsTranslucent
.insert({
129 Element::SelectionText
,
130 Element::SelectionBack
,
131 Element::SelectionAdditionalText
,
132 Element::SelectionAdditionalBack
,
133 Element::SelectionSecondaryText
,
134 Element::SelectionSecondaryBack
,
135 Element::SelectionInactiveText
,
136 Element::SelectionBack
,
137 Element::SelectionInactiveBack
,
140 foldmarginColour
.reset();
141 foldmarginHighlightColour
.reset();
143 controlCharSymbol
= 0; /* Draw the control characters */
144 controlCharWidth
= 0;
145 selbar
= Platform::Chrome();
146 selbarlight
= Platform::ChromeHighlight();
147 styles
[StyleLineNumber
].fore
= ColourRGBA(0, 0, 0);
148 styles
[StyleLineNumber
].back
= Platform::Chrome();
150 elementBaseColours
[Element::Caret
] = ColourRGBA(0, 0, 0);
151 elementBaseColours
[Element::CaretAdditional
] = ColourRGBA(0x7f, 0x7f, 0x7f);
152 elementAllowsTranslucent
.insert({
154 Element::CaretAdditional
,
157 elementColours
.erase(Element::CaretLineBack
);
158 elementAllowsTranslucent
.insert(Element::CaretLineBack
);
160 someStylesProtected
= false;
161 someStylesForceCase
= false;
163 hotspotUnderline
= true;
164 elementColours
.erase(Element::HotSpotActive
);
165 elementAllowsTranslucent
.insert(Element::HotSpotActive
);
168 rightMarginWidth
= 1;
169 ms
.resize(MaxMargin
+ 1);
170 ms
[0] = MarginStyle(MarginType::Number
);
171 ms
[1] = MarginStyle(MarginType::Symbol
, 16, ~MaskFolders
);
172 ms
[2] = MarginStyle(MarginType::Symbol
);
174 CalculateMarginWidthAndMask();
175 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
177 viewWhitespace
= WhiteSpace::Invisible
;
178 tabDrawMode
= TabDrawMode::LongArrow
;
180 elementColours
.erase(Element::WhiteSpace
);
181 elementAllowsTranslucent
.insert(Element::WhiteSpace
);
183 viewIndentationGuides
= IndentView::None
;
185 extraFontFlag
= FontQuality::QualityDefault
;
188 marginStyleOffset
= 0;
189 annotationVisible
= AnnotationVisible::Hidden
;
190 annotationStyleOffset
= 0;
191 eolAnnotationVisible
= EOLAnnotationVisible::Hidden
;
192 eolAnnotationStyleOffset
= 0;
193 braceHighlightIndicatorSet
= false;
194 braceHighlightIndicator
= 0;
195 braceBadLightIndicatorSet
= false;
196 braceBadLightIndicator
= 0;
198 edgeState
= EdgeVisualStyle::None
;
199 theEdge
= EdgeProperties(0, ColourRGBA(0xc0, 0xc0, 0xc0));
201 marginNumberPadding
= 3;
202 ctrlCharPadding
= 3; // +3 For a blank on front and rounded edge each side
203 lastSegItalicsOffset
= 2;
205 localeName
= localeNameDefault
;
208 // Copy constructor only called when printing copies the screen ViewStyle so it can be
209 // modified for printing styles.
210 ViewStyle::ViewStyle(const ViewStyle
&source
) : ViewStyle(source
.styles
.size()) {
211 styles
= source
.styles
;
212 for (Style
&style
: styles
) {
213 // Can't just copy fontName as its lifetime is relative to its owning ViewStyle
214 style
.fontName
= fontNames
.Save(style
.fontName
);
216 nextExtendedStyle
= source
.nextExtendedStyle
;
217 markers
= source
.markers
;
218 CalcLargestMarkerHeight();
220 indicators
= source
.indicators
;
222 indicatorsDynamic
= source
.indicatorsDynamic
;
223 indicatorsSetFore
= source
.indicatorsSetFore
;
225 selection
= source
.selection
;
227 foldmarginColour
= source
.foldmarginColour
;
228 foldmarginHighlightColour
= source
.foldmarginHighlightColour
;
230 hotspotUnderline
= source
.hotspotUnderline
;
232 controlCharSymbol
= source
.controlCharSymbol
;
233 controlCharWidth
= source
.controlCharWidth
;
234 selbar
= source
.selbar
;
235 selbarlight
= source
.selbarlight
;
236 caret
= source
.caret
;
237 caretLine
= source
.caretLine
;
238 someStylesProtected
= false;
239 someStylesForceCase
= false;
240 leftMarginWidth
= source
.leftMarginWidth
;
241 rightMarginWidth
= source
.rightMarginWidth
;
243 maskInLine
= source
.maskInLine
;
244 maskDrawInText
= source
.maskDrawInText
;
245 fixedColumnWidth
= source
.fixedColumnWidth
;
246 marginInside
= source
.marginInside
;
247 textStart
= source
.textStart
;
248 zoomLevel
= source
.zoomLevel
;
249 viewWhitespace
= source
.viewWhitespace
;
250 tabDrawMode
= source
.tabDrawMode
;
251 whitespaceSize
= source
.whitespaceSize
;
252 viewIndentationGuides
= source
.viewIndentationGuides
;
253 viewEOL
= source
.viewEOL
;
254 extraFontFlag
= source
.extraFontFlag
;
255 extraAscent
= source
.extraAscent
;
256 extraDescent
= source
.extraDescent
;
257 marginStyleOffset
= source
.marginStyleOffset
;
258 annotationVisible
= source
.annotationVisible
;
259 annotationStyleOffset
= source
.annotationStyleOffset
;
260 eolAnnotationVisible
= source
.eolAnnotationVisible
;
261 eolAnnotationStyleOffset
= source
.eolAnnotationStyleOffset
;
262 braceHighlightIndicatorSet
= source
.braceHighlightIndicatorSet
;
263 braceHighlightIndicator
= source
.braceHighlightIndicator
;
264 braceBadLightIndicatorSet
= source
.braceBadLightIndicatorSet
;
265 braceBadLightIndicator
= source
.braceBadLightIndicator
;
267 edgeState
= source
.edgeState
;
268 theEdge
= source
.theEdge
;
269 theMultiEdge
= source
.theMultiEdge
;
271 marginNumberPadding
= source
.marginNumberPadding
;
272 ctrlCharPadding
= source
.ctrlCharPadding
;
273 lastSegItalicsOffset
= source
.lastSegItalicsOffset
;
277 localeName
= source
.localeName
;
280 ViewStyle::~ViewStyle() = default;
282 void ViewStyle::CalculateMarginWidthAndMask() noexcept
{
283 fixedColumnWidth
= marginInside
? leftMarginWidth
: 0;
284 maskInLine
= 0xffffffff;
285 int maskDefinedMarkers
= 0;
286 for (const MarginStyle
&m
: ms
) {
287 fixedColumnWidth
+= m
.width
;
289 maskInLine
&= ~m
.mask
;
290 maskDefinedMarkers
|= m
.mask
;
293 for (int markBit
= 0; markBit
< 32; markBit
++) {
294 const int maskBit
= 1U << markBit
;
295 switch (markers
[markBit
].markType
) {
296 case MarkerSymbol::Empty
:
297 maskInLine
&= ~maskBit
;
299 case MarkerSymbol::Background
:
300 case MarkerSymbol::Underline
:
301 maskInLine
&= ~maskBit
;
302 maskDrawInText
|= maskDefinedMarkers
& maskBit
;
304 default: // Other marker types do not affect the masks
310 void ViewStyle::Refresh(Surface
&surface
, int tabInChars
) {
313 selbar
= Platform::Chrome();
314 selbarlight
= Platform::ChromeHighlight();
316 // Apply the extra font flag which controls text drawing quality to each style.
317 for (Style
&style
: styles
) {
318 style
.extraFontFlag
= extraFontFlag
;
321 // Create a FontRealised object for each unique font in the styles.
322 CreateAndAddFont(styles
[StyleDefault
]);
323 for (const Style
&style
: styles
) {
324 CreateAndAddFont(style
);
327 // Ask platform to allocate each unique font.
328 for (const std::pair
<const FontSpecification
, std::unique_ptr
<FontRealised
>> &font
: fonts
) {
329 font
.second
->Realise(surface
, zoomLevel
, technology
, font
.first
, localeName
.c_str());
332 // Set the platform font handle and measurements for each style.
333 for (Style
&style
: styles
) {
334 const FontRealised
*fr
= Find(style
);
335 style
.Copy(fr
->font
, fr
->measurements
);
338 indicatorsDynamic
= std::any_of(indicators
.cbegin(), indicators
.cend(),
339 [](const Indicator
&indicator
) noexcept
{ return indicator
.IsDynamic(); });
341 indicatorsSetFore
= std::any_of(indicators
.cbegin(), indicators
.cend(),
342 [](const Indicator
&indicator
) noexcept
{ return indicator
.OverridesTextFore(); });
346 FindMaxAscentDescent();
347 // Ensure reasonable values: lines less than 1 pixel high will not work
348 maxAscent
= std::max(1.0, maxAscent
+ extraAscent
);
349 maxDescent
= std::max(0.0, maxDescent
+ extraDescent
);
350 lineHeight
= static_cast<int>(std::lround(maxAscent
+ maxDescent
));
351 lineOverlap
= lineHeight
/ 10;
354 if (lineOverlap
> lineHeight
)
355 lineOverlap
= lineHeight
;
357 someStylesProtected
= std::any_of(styles
.cbegin(), styles
.cend(),
358 [](const Style
&style
) noexcept
{ return style
.IsProtected(); });
360 someStylesForceCase
= std::any_of(styles
.cbegin(), styles
.cend(),
361 [](const Style
&style
) noexcept
{ return style
.caseForce
!= Style::CaseForce::mixed
; });
363 aveCharWidth
= styles
[StyleDefault
].aveCharWidth
;
364 spaceWidth
= styles
[StyleDefault
].spaceWidth
;
365 tabWidth
= spaceWidth
* tabInChars
;
367 controlCharWidth
= 0.0;
368 if (controlCharSymbol
>= 32) {
369 const char cc
[2] = { static_cast<char>(controlCharSymbol
), '\0' };
370 controlCharWidth
= surface
.WidthText(styles
[StyleControlChar
].font
.get(), cc
);
373 CalculateMarginWidthAndMask();
374 textStart
= marginInside
? fixedColumnWidth
: leftMarginWidth
;
377 void ViewStyle::ReleaseAllExtendedStyles() noexcept
{
378 nextExtendedStyle
= 256;
381 int ViewStyle::AllocateExtendedStyles(int numberStyles
) {
382 const int startRange
= nextExtendedStyle
;
383 nextExtendedStyle
+= numberStyles
;
384 EnsureStyle(nextExtendedStyle
);
388 void ViewStyle::EnsureStyle(size_t index
) {
389 if (index
>= styles
.size()) {
390 AllocStyles(index
+1);
394 void ViewStyle::ResetDefaultStyle() {
395 styles
[StyleDefault
] = Style(fontNames
.Save(Platform::DefaultFont()));
398 void ViewStyle::ClearStyles() {
399 // Reset all styles to be like the default style
400 for (size_t i
=0; i
<styles
.size(); i
++) {
401 if (i
!= StyleDefault
) {
402 styles
[i
] = styles
[StyleDefault
];
405 styles
[StyleLineNumber
].back
= Platform::Chrome();
407 // Set call tip fore/back to match the values previously set for call tips
408 styles
[StyleCallTip
].back
= ColourRGBA(0xff, 0xff, 0xff);
409 styles
[StyleCallTip
].fore
= ColourRGBA(0x80, 0x80, 0x80);
412 void ViewStyle::SetStyleFontName(int styleIndex
, const char *name
) {
413 styles
[styleIndex
].fontName
= fontNames
.Save(name
);
416 void ViewStyle::SetFontLocaleName(const char *name
) {
420 bool ViewStyle::ProtectionActive() const noexcept
{
421 return someStylesProtected
;
424 int ViewStyle::ExternalMarginWidth() const noexcept
{
425 return marginInside
? 0 : fixedColumnWidth
;
428 int ViewStyle::MarginFromLocation(Point pt
) const noexcept
{
429 XYPOSITION x
= marginInside
? 0 : -fixedColumnWidth
;
430 for (size_t i
= 0; i
< ms
.size(); i
++) {
431 if ((pt
.x
>= x
) && (pt
.x
< x
+ ms
[i
].width
))
432 return static_cast<int>(i
);
438 bool ViewStyle::ValidStyle(size_t styleIndex
) const noexcept
{
439 return styleIndex
< styles
.size();
442 void ViewStyle::CalcLargestMarkerHeight() noexcept
{
443 largestMarkerHeight
= 0;
444 for (const LineMarker
&marker
: markers
) {
445 switch (marker
.markType
) {
446 case MarkerSymbol::Pixmap
:
447 if (marker
.pxpm
&& marker
.pxpm
->GetHeight() > largestMarkerHeight
)
448 largestMarkerHeight
= marker
.pxpm
->GetHeight();
450 case MarkerSymbol::RgbaImage
:
451 if (marker
.image
&& marker
.image
->GetHeight() > largestMarkerHeight
)
452 largestMarkerHeight
= marker
.image
->GetHeight();
454 default: // Only images have their own natural heights
460 int ViewStyle::GetFrameWidth() const noexcept
{
461 return std::clamp(caretLine
.frame
, 1, lineHeight
/ 3);
464 bool ViewStyle::IsLineFrameOpaque(bool caretActive
, bool lineContainsCaret
) const {
465 return caretLine
.frame
&& (caretActive
|| caretLine
.alwaysShow
) &&
466 ElementColour(Element::CaretLineBack
) &&
467 (caretLine
.layer
== Layer::Base
) && lineContainsCaret
;
470 // See if something overrides the line background colour: Either if caret is on the line
471 // and background colour is set for that, or if a marker is defined that forces its background
472 // colour onto the line, or if a marker is defined but has no selection margin in which to
473 // display itself (as long as it's not an MarkerSymbol::Empty marker). These are checked in order
474 // with the earlier taking precedence. When multiple markers cause background override,
475 // the colour for the highest numbered one is used.
476 std::optional
<ColourRGBA
> ViewStyle::Background(int marksOfLine
, bool caretActive
, bool lineContainsCaret
) const {
477 std::optional
<ColourRGBA
> background
;
478 if (!caretLine
.frame
&& (caretActive
|| caretLine
.alwaysShow
) &&
479 (caretLine
.layer
== Layer::Base
) && lineContainsCaret
) {
480 background
= ElementColour(Element::CaretLineBack
);
482 if (!background
&& marksOfLine
) {
483 int marks
= marksOfLine
;
484 for (int markBit
= 0; (markBit
< 32) && marks
; markBit
++) {
485 if ((marks
& 1) && (markers
[markBit
].markType
== MarkerSymbol::Background
) &&
486 (markers
[markBit
].layer
== Layer::Base
)) {
487 background
= markers
[markBit
].back
;
492 if (!background
&& maskInLine
) {
493 int marksMasked
= marksOfLine
& maskInLine
;
495 for (int markBit
= 0; (markBit
< 32) && marksMasked
; markBit
++) {
496 if ((marksMasked
& 1) &&
497 (markers
[markBit
].layer
== Layer::Base
)) {
498 background
= markers
[markBit
].back
;
505 return background
->Opaque();
511 bool ViewStyle::SelectionBackgroundDrawn() const noexcept
{
512 return selection
.layer
== Layer::Base
;
515 bool ViewStyle::SelectionTextDrawn() const {
517 ElementIsSet(Element::SelectionText
) ||
518 ElementIsSet(Element::SelectionAdditionalText
) ||
519 ElementIsSet(Element::SelectionSecondaryText
) ||
520 ElementIsSet(Element::SelectionInactiveText
);
523 bool ViewStyle::WhitespaceBackgroundDrawn() const {
524 return (viewWhitespace
!= WhiteSpace::Invisible
) && (ElementIsSet(Element::WhiteSpaceBack
));
527 bool ViewStyle::WhiteSpaceVisible(bool inIndent
) const noexcept
{
528 return (!inIndent
&& viewWhitespace
== WhiteSpace::VisibleAfterIndent
) ||
529 (inIndent
&& viewWhitespace
== WhiteSpace::VisibleOnlyInIndent
) ||
530 viewWhitespace
== WhiteSpace::VisibleAlways
;
533 ColourRGBA
ViewStyle::WrapColour() const {
534 return ElementColour(Element::WhiteSpace
).value_or(styles
[StyleDefault
].fore
);
537 // Insert new edge in sorted order.
538 void ViewStyle::AddMultiEdge(int column
, ColourRGBA colour
) {
540 std::upper_bound(theMultiEdge
.begin(), theMultiEdge
.end(), column
,
541 [](const EdgeProperties
&a
, const EdgeProperties
&b
) noexcept
{
542 return a
.column
< b
.column
;
544 EdgeProperties(column
, colour
));
547 std::optional
<ColourRGBA
> ViewStyle::ElementColour(Element element
) const {
548 ElementMap::const_iterator search
= elementColours
.find(element
);
549 if (search
!= elementColours
.end()) {
550 if (search
->second
.has_value()) {
551 return search
->second
;
554 ElementMap::const_iterator searchBase
= elementBaseColours
.find(element
);
555 if (searchBase
!= elementBaseColours
.end()) {
556 if (searchBase
->second
.has_value()) {
557 return searchBase
->second
;
563 bool ViewStyle::ElementAllowsTranslucent(Element element
) const {
564 return elementAllowsTranslucent
.count(element
) > 0;
567 bool ViewStyle::ResetElement(Element element
) {
568 ElementMap::const_iterator search
= elementColours
.find(element
);
569 const bool changed
= (search
!= elementColours
.end()) && (search
->second
.has_value());
570 elementColours
.erase(element
);
574 bool ViewStyle::SetElementColour(Element element
, ColourRGBA colour
) {
575 ElementMap::const_iterator search
= elementColours
.find(element
);
577 (search
== elementColours
.end()) ||
578 (search
->second
.has_value() && !(*search
->second
== colour
));
579 elementColours
[element
] = colour
;
583 bool ViewStyle::SetElementColourOptional(Element element
, uptr_t wParam
, sptr_t lParam
) {
585 return SetElementColour(element
, ColourRGBA::FromIpRGB(lParam
));
587 return ResetElement(element
);
591 void ViewStyle::SetElementRGB(Element element
, int rgb
) {
592 const ColourRGBA current
= ElementColour(element
).value_or(ColourRGBA(0, 0, 0, 0));
593 elementColours
[element
] = ColourRGBA(ColourRGBA(rgb
), current
.GetAlpha());
596 void ViewStyle::SetElementAlpha(Element element
, int alpha
) {
597 const ColourRGBA current
= ElementColour(element
).value_or(ColourRGBA(0, 0, 0, 0));
598 elementColours
[element
] = ColourRGBA(current
, std::min(alpha
, 0xff));
601 bool ViewStyle::ElementIsSet(Element element
) const {
602 ElementMap::const_iterator search
= elementColours
.find(element
);
603 if (search
!= elementColours
.end()) {
604 return search
->second
.has_value();
609 bool ViewStyle::SetElementBase(Element element
, ColourRGBA colour
) {
610 ElementMap::const_iterator search
= elementBaseColours
.find(element
);
612 (search
== elementBaseColours
.end()) ||
613 (search
->second
.has_value() && !(*search
->second
== colour
));
614 elementBaseColours
[element
] = colour
;
618 bool ViewStyle::SetWrapState(Wrap wrapState_
) noexcept
{
619 const bool changed
= wrap
.state
!= wrapState_
;
620 wrap
.state
= wrapState_
;
624 bool ViewStyle::SetWrapVisualFlags(WrapVisualFlag wrapVisualFlags_
) noexcept
{
625 const bool changed
= wrap
.visualFlags
!= wrapVisualFlags_
;
626 wrap
.visualFlags
= wrapVisualFlags_
;
630 bool ViewStyle::SetWrapVisualFlagsLocation(WrapVisualLocation wrapVisualFlagsLocation_
) noexcept
{
631 const bool changed
= wrap
.visualFlagsLocation
!= wrapVisualFlagsLocation_
;
632 wrap
.visualFlagsLocation
= wrapVisualFlagsLocation_
;
636 bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_
) noexcept
{
637 const bool changed
= wrap
.visualStartIndent
!= wrapVisualStartIndent_
;
638 wrap
.visualStartIndent
= wrapVisualStartIndent_
;
642 bool ViewStyle::SetWrapIndentMode(WrapIndentMode wrapIndentMode_
) noexcept
{
643 const bool changed
= wrap
.indentMode
!= wrapIndentMode_
;
644 wrap
.indentMode
= wrapIndentMode_
;
648 bool ViewStyle::IsBlockCaretStyle() const noexcept
{
649 return ((caret
.style
& CaretStyle::InsMask
) == CaretStyle::Block
) ||
650 FlagSet(caret
.style
, CaretStyle::OverstrikeBlock
) ||
651 FlagSet(caret
.style
, CaretStyle::Curses
);
654 bool ViewStyle::IsCaretVisible(bool isMainSelection
) const noexcept
{
655 return caret
.width
> 0 &&
656 ((caret
.style
& CaretStyle::InsMask
) != CaretStyle::Invisible
||
657 (FlagSet(caret
.style
, CaretStyle::Curses
) && !isMainSelection
)); // only draw additional selections in curses mode
660 bool ViewStyle::DrawCaretInsideSelection(bool inOverstrike
, bool imeCaretBlockOverride
) const noexcept
{
661 if (FlagSet(caret
.style
, CaretStyle::BlockAfter
))
663 return ((caret
.style
& CaretStyle::InsMask
) == CaretStyle::Block
) ||
664 (inOverstrike
&& FlagSet(caret
.style
, CaretStyle::OverstrikeBlock
)) ||
665 imeCaretBlockOverride
||
666 FlagSet(caret
.style
, CaretStyle::Curses
);
669 ViewStyle::CaretShape
ViewStyle::CaretShapeForMode(bool inOverstrike
, bool isMainSelection
) const noexcept
{
671 return (FlagSet(caret
.style
, CaretStyle::OverstrikeBlock
)) ? CaretShape::block
: CaretShape::bar
;
674 if (FlagSet(caret
.style
, CaretStyle::Curses
) && !isMainSelection
) {
675 return CaretShape::block
;
678 const CaretStyle caretStyle
= caret
.style
& CaretStyle::InsMask
;
679 return (caretStyle
<= CaretStyle::Block
) ? static_cast<CaretShape
>(caretStyle
) : CaretShape::line
;
682 void ViewStyle::AllocStyles(size_t sizeNew
) {
683 size_t i
=styles
.size();
684 styles
.resize(sizeNew
);
685 if (styles
.size() > StyleDefault
) {
686 for (; i
<sizeNew
; i
++) {
687 if (i
!= StyleDefault
) {
688 styles
[i
] = styles
[StyleDefault
];
694 void ViewStyle::CreateAndAddFont(const FontSpecification
&fs
) {
696 FontMap::iterator it
= fonts
.find(fs
);
697 if (it
== fonts
.end()) {
698 fonts
[fs
] = std::make_unique
<FontRealised
>();
703 FontRealised
*ViewStyle::Find(const FontSpecification
&fs
) {
704 if (!fs
.fontName
) // Invalid specification so return arbitrary object
705 return fonts
.begin()->second
.get();
706 FontMap::iterator it
= fonts
.find(fs
);
707 if (it
!= fonts
.end()) {
708 // Should always reach here since map was just set for all styles
709 return it
->second
.get();
714 void ViewStyle::FindMaxAscentDescent() noexcept
{
715 for (const auto &font
: fonts
) {
716 if (maxAscent
< font
.second
->measurements
.ascent
)
717 maxAscent
= font
.second
->measurements
.ascent
;
718 if (maxDescent
< font
.second
->measurements
.descent
)
719 maxDescent
= font
.second
->measurements
.descent
;