4#include "Stroika/Frameworks/StroikaPreComp.h"
6#include "Stroika/Frameworks/Led/GDI.h"
13using namespace Stroika::Frameworks;
14using namespace Stroika::Frameworks::Led;
16#if qStroika_Frameworks_Led_SupportGDI
23#if qStroika_Platform_Windows
25 inline bool LogFontsEqual_ (LOGFONT lhs, LOGFONT rhs)
27 size_t bytesToCompare = offsetof (LOGFONT, lfFaceName) + (::_tcslen (lhs.lfFaceName) + 1) *
sizeof (
Characters::SDKChar);
28 Require (bytesToCompare <=
sizeof (LOGFONT));
29 return ::memcmp (&lhs, &rhs, bytesToCompare) == 0;
33 if (lhs.GetStyle_SubOrSuperScript () == rhs.GetStyle_SubOrSuperScript ()) {
35 lhs.GetOSRep (&lhslf);
37 rhs.GetOSRep (&rhslf);
38 return LogFontsEqual_ (lhslf, rhslf);
46TextImager::FontCacheInfoUpdater::FontCacheInfoUpdater (
const TextImager* imager, Tablet* tablet,
const FontSpecification& fontSpec)
48#if qStroika_Platform_Windows
50 , fRestoreObject{nullptr}
51 , fRestoreAttribObject{nullptr}
54#if qStroika_Platform_Windows
62 if (imager->fCachedFont ==
nullptr or !LogFontsEqual_ (fontSpec, imager->fCachedFontSpec)) {
64 delete imager->fCachedFont;
65 imager->fCachedFont =
nullptr;
66 imager->fCachedFont =
new FontObject ();
68 fontSpec.GetOSRep (&lf);
69 if (fontSpec.GetStyle_SubOrSuperScript () != FontSpecification::eNoSubOrSuperscript) {
72 lf.lfHeight = lf.lfHeight * 2 / 3;
73 if (lf.lfHeight == 0) {
77 Verify (imager->fCachedFont->CreateFontIndirect (&lf));
78 imager->fCachedFontSpec = fontSpec;
84 if (tablet->m_hDC != tablet->m_hAttribDC) {
85 fRestoreObject = ::SelectObject (tablet->m_hDC, imager->fCachedFont->m_hObject);
89 if (tablet->m_hAttribDC !=
nullptr) {
90 fRestoreAttribObject = ::SelectObject (tablet->m_hAttribDC, imager->fCachedFont->m_hObject);
95 imager->fCachedFontInfo = tablet->GetFontMetrics ();
105TextImager::TextImager ()
106 : fTextStore{nullptr}
107 , fDefaultFont{GetStaticDefaultFont ()}
108 , fForceAllRowsShowing{true}
109 , fImageUsingOffscreenBitmaps{qStroika_Frameworks_Led_UseOffscreenBitmapsToReduceFlicker}
111 , fSuppressGoalColumnRecompute{false}
112 , fSelectionGoalColumn{TWIPS{0}}
113 , fUseEOLBOLRowHilightStyle{true}
114 , fSelectionShown{false}
115 , fWindowRect{Led_Rect{0, 0, 0, 0}}
116 , fHiliteMarker{nullptr}
117 , fWeAllocedHiliteMarker{false}
121#if qStroika_Platform_Windows
124 fCachedFontValid{false}
127 for (
Color** i = &fDefaultColorIndex[0]; i < &fDefaultColorIndex[eMaxDefaultColorIndex]; ++i) {
132TextImager::~TextImager ()
134 Require (fTextStore ==
nullptr);
135 Require (fHiliteMarker ==
nullptr);
136 for (
Color** i = &fDefaultColorIndex[0]; i < &fDefaultColorIndex[eMaxDefaultColorIndex]; ++i) {
140#if qStroika_Platform_Windows
145TextStore* TextImager::PeekAtTextStore ()
const
157void TextImager::SpecifyTextStore (TextStore* useTextStore)
159 if (fTextStore != useTextStore) {
160 if (fTextStore !=
nullptr) {
161 HookLosingTextStore ();
163 fTextStore = useTextStore;
164 if (fTextStore !=
nullptr) {
165 HookGainedNewTextStore ();
170void TextImager::HookLosingTextStore ()
172 HookLosingTextStore_ ();
175void TextImager::HookLosingTextStore_ ()
179 if (fHiliteMarker !=
nullptr) {
180 Assert (fWeAllocedHiliteMarker);
181 GetTextStore ().RemoveMarker (fHiliteMarker);
182 delete fHiliteMarker;
183 fHiliteMarker =
nullptr;
184 fTextStore->RemoveMarkerOwner (
this);
188void TextImager::HookGainedNewTextStore ()
190 HookGainedNewTextStore_ ();
193void TextImager::HookGainedNewTextStore_ ()
196 if (fHiliteMarker ==
nullptr) {
197 fTextStore->AddMarkerOwner (
this);
198 SetHilightMarker (
nullptr);
209void TextImager::PurgeUnneededMemory ()
211 InvalidateAllCaches ();
221void TextImager::InvalidateAllCaches ()
224#if qStroika_Platform_Windows
226 fCachedFont =
nullptr;
228 fCachedFontValid =
false;
243void TextImager::SetDefaultFont (
const IncrementalFontSpecification& defaultFont)
245 SetDefaultFont_ (defaultFont);
248void TextImager::SetDefaultFont_ (
const IncrementalFontSpecification& defaultFont)
250 if (PeekAtTextStore () !=
nullptr) {
251 TextStore::SimpleUpdater u (GetTextStore (), 0, GetTextStore ().GetEnd (),
false);
252 fDefaultFont.MergeIn (defaultFont);
253 if (defaultFont.GetTextColor_Valid ()) {
254 SetDefaultTextColor (eDefaultTextColor, defaultFont.GetTextColor ());
258 fDefaultFont.MergeIn (defaultFont);
259 if (defaultFont.GetTextColor_Valid ()) {
260 SetDefaultTextColor (eDefaultTextColor, defaultFont.GetTextColor ());
281 return GetDefaultFont ();
284void TextImager::SetSelectionShown (
bool shown)
286 fSelectionShown = shown;
297const TabStopList& TextImager::GetTabStopList (
size_t )
const
300 static SimpleTabStopList sDefaultTabStopList = SimpleTabStopList (TWIPS (1440 / 3));
301 return sDefaultTabStopList;
308void TextImager::SetWindowRect (
const Led_Rect& windowRect)
310 SetWindowRect_ (windowRect);
318void TextImager::ScrollSoShowingHHelper (
size_t markerPos,
size_t andTryToShowMarkerPos)
320 CoordinateType maxHScrollPos = ComputeMaxHScrollPos ();
321 CoordinateType hsp = GetHScrollPos ();
326 if (maxHScrollPos == 0) {
333 Led_Rect windowRect = GetWindowRect ();
337 Led_Rect andTryRRR = GetCharWindowLocation (andTryToShowMarkerPos);
338 CoordinateType whereAtInGlobalCoords = windowRect.left - andTryRRR.left;
339 if (andTryRRR.left < windowRect.left) {
340 Assert (hsp >= whereAtInGlobalCoords);
341 hsp -= whereAtInGlobalCoords;
343 else if (andTryRRR.right > windowRect.right) {
344 CoordinateType howFarOffRight = andTryRRR.right - windowRect.right;
345 hsp += howFarOffRight;
346 hsp = min (hsp, maxHScrollPos);
350 Assert (hsp <= maxHScrollPos);
354 Led_Rect rrr = GetCharWindowLocation (markerPos);
357 CoordinateType adjustRRRBy = GetHScrollPos () - hsp;
358 rrr += Led_Point (0, adjustRRRBy);
361 CoordinateType whereAtInGlobalCoords = windowRect.GetLeft () - rrr.GetLeft ();
362 if (rrr.GetLeft () < windowRect.GetLeft ()) {
363 Assert (hsp >= whereAtInGlobalCoords);
364 hsp -= whereAtInGlobalCoords;
366 else if (rrr.GetRight () > windowRect.GetRight ()) {
367 CoordinateType howFarOffRight = rrr.GetRight () - windowRect.GetRight ();
368 hsp += howFarOffRight;
369 hsp = min (hsp, maxHScrollPos);
374 Assert (hsp <= maxHScrollPos);
378void TextImager::SetHScrollPos (CoordinateType hScrollPos)
380 if (hScrollPos != GetHScrollPos ()) {
381 SetHScrollPos_ (hScrollPos);
382 InvalidateScrollBarParameters ();
403DistanceType TextImager::ComputeMaxHScrollPos ()
const
413DistanceType TextImager::CalculateLongestRowInWindowPixelWidth ()
const
415 size_t startOfWindow = GetMarkerPositionOfStartOfWindow ();
416 size_t endOfWindow = GetMarkerPositionOfEndOfWindow ();
418 DistanceType longestRowWidth = 0;
419 for (
size_t curOffset = startOfWindow; curOffset < endOfWindow;) {
420 DistanceType thisRowWidth = CalcSegmentSize (curOffset, GetEndOfRowContainingPosition (curOffset));
421 longestRowWidth = max (longestRowWidth, thisRowWidth);
423 size_t newOffset = GetStartOfNextRowFromRowContainingPosition (curOffset);
424 if (newOffset <= curOffset) {
427 curOffset = newOffset;
430 return longestRowWidth;
439void TextImager::TabletChangedMetrics ()
441 InvalidateAllCaches ();
444void TextImager::SetSelection (
size_t start,
size_t end)
447 Assert (end <= GetEnd ());
448 if (start != GetSelectionStart () or end != GetSelectionEnd ()) {
449 SetSelection_ (start, end);
453size_t TextImager::GetSelectionStart ()
const
456 return (fHiliteMarker->GetStart ());
459size_t TextImager::GetSelectionEnd ()
const
462 return (fHiliteMarker->GetEnd ());
465void TextImager::GetSelection (
size_t* start,
size_t* end)
const
470 fHiliteMarker->GetRange (start, end);
473void TextImager::SetSelection_ (
size_t start,
size_t end)
475 Require (start >= 0);
476 Require (end <= GetEnd ());
477 Require (start <= end);
478 GetTextStore ().SetMarkerRange (fHiliteMarker, start, end);
481void TextImager::SetHilightMarker (HilightMarker* newHilightMarker)
485 if (fHiliteMarker !=
nullptr) {
486 fHiliteMarker->GetRange (&start, &end);
487 GetTextStore ().RemoveMarker (fHiliteMarker);
488 if (fWeAllocedHiliteMarker) {
489 delete fHiliteMarker;
492 fHiliteMarker = newHilightMarker;
493 fWeAllocedHiliteMarker = bool (fHiliteMarker ==
nullptr);
494 if (fHiliteMarker ==
nullptr) {
495 fHiliteMarker =
new HilightMarker ();
498 GetTextStore ().AddMarker (fHiliteMarker, start, end - start,
this);
505void TextImager::RecomputeSelectionGoalColumn ()
507 if (not fSuppressGoalColumnRecompute) {
511 DistanceType lhs = 0;
512 DistanceType rhs = 0;
513 GetRowRelativeCharLoc (GetSelectionStart (), &lhs, &rhs);
514 SetSelectionGoalColumn (Tablet_Acquirer (
this)->CvtToTWIPSH (lhs + (rhs - lhs) / 2));
522size_t TextImager::ComputeRelativePosition (
size_t fromPos, CursorMovementDirection direction, CursorMovementUnit movementUnit)
530 switch (movementUnit) {
531 case eCursorByChar: {
532 return (FindPreviousCharacter (fromPos));
535 case eCursorByWord: {
536 return (GetTextStore ().FindFirstWordStartStrictlyBeforePosition (fromPos));
540 size_t startOfStartRow = GetStartOfRowContainingPosition (fromPos);
541 size_t startOfPrevRow = GetStartOfPrevRowFromRowContainingPosition (fromPos);
542 if (startOfStartRow == startOfPrevRow) {
547 return GetRowRelativeCharAtLoc (Tablet_Acquirer (
this)->CvtFromTWIPSH (GetSelectionGoalColumn ()), startOfPrevRow);
551 case eCursorByLine: {
552 size_t fromLine = GetTextStore ().GetLineContainingPosition (fromPos);
553 size_t newLine = (fromLine > 0) ? fromLine - 1 : 0;
554 if (newLine == fromLine) {
560 size_t positionInLine = fromPos - GetTextStore ().GetStartOfLine (fromLine);
561 Assert (positionInLine <= GetTextStore ().GetLineLength (fromLine));
562 positionInLine = min (positionInLine, GetTextStore ().GetLineLength (newLine));
563 return GetTextStore ().GetStartOfLine (newLine) + positionInLine;
567 case eCursorByWindow: {
571 case eCursorByBuffer: {
580 case eCursorForward: {
581 switch (movementUnit) {
582 case eCursorByChar: {
583 return (FindNextCharacter (fromPos));
586 case eCursorByWord: {
587 TextStore& ts = GetTextStore ();
588 return (ts.FindFirstWordStartAfterPosition (ts.FindNextCharacter (fromPos)));
592 size_t startOfStartRow = GetStartOfRowContainingPosition (fromPos);
593 size_t startOfNextRow = GetStartOfNextRowFromRowContainingPosition (fromPos);
594 if (startOfStartRow == startOfNextRow) {
599 return GetRowRelativeCharAtLoc (Tablet_Acquirer (
this)->CvtFromTWIPSH (GetSelectionGoalColumn ()), startOfNextRow);
603 case eCursorByLine: {
604 size_t fromLine = GetTextStore ().GetLineContainingPosition (fromPos);
605 size_t newLine = (fromLine == GetTextStore ().GetLineCount () - 1) ? fromLine : (fromLine + 1);
606 Assert (newLine <= GetTextStore ().GetLineCount () - 1);
607 if (newLine == fromLine) {
613 size_t positionInLine = fromPos - GetTextStore ().GetStartOfLine (fromLine);
614 Assert (positionInLine <= GetTextStore ().GetLineLength (fromLine));
615 positionInLine = min (positionInLine, GetTextStore ().GetLineLength (newLine));
616 return (GetTextStore ().GetStartOfLine (newLine) + positionInLine);
620 case eCursorByWindow: {
624 case eCursorByBuffer: {
633 case eCursorToStart: {
634 switch (movementUnit) {
635 case eCursorByChar: {
639 case eCursorByWord: {
640 return (GetTextStore ().FindFirstWordStartStrictlyBeforePosition (FindNextCharacter (fromPos)));
644 size_t fromRow = GetRowContainingPosition (fromPos);
645 return (GetStartOfRow (fromRow));
648 case eCursorByLine: {
649 size_t result = GetTextStore ().GetStartOfLineContainingPosition (fromPos);
650 if (fromPos == result) {
651 result = GetTextStore ().GetStartOfLineContainingPosition (GetTextStore ().FindPreviousCharacter (result));
656 case eCursorByWindow: {
657 return (GetMarkerPositionOfStartOfWindow ());
660 case eCursorByBuffer: {
670 switch (movementUnit) {
671 case eCursorByChar: {
675 case eCursorByWord: {
676 return (GetTextStore ().FindFirstWordEndAfterPosition (fromPos));
680 size_t fromRow = GetRowContainingPosition (fromPos);
681 return (GetEndOfRow (fromRow));
684 case eCursorByLine: {
685 size_t result = GetTextStore ().GetEndOfLineContainingPosition (fromPos);
686 if (fromPos == result) {
687 result = GetTextStore ().GetEndOfLineContainingPosition (GetTextStore ().FindNextCharacter (result));
692 case eCursorByWindow: {
693 return (GetMarkerPositionOfEndOfWindow ());
696 case eCursorByBuffer: {
697 return (GetTextStore ().GetEnd ());
724Led_Rect TextImager::GetTextWindowBoundingRect (
size_t fromMarkerPos,
size_t toMarkerPos)
const
726 Require (fromMarkerPos <= toMarkerPos);
728 Led_Rect windowRect = GetWindowRect ();
729 Led_Rect r1 = GetCharWindowLocation (fromMarkerPos);
731 if (fromMarkerPos != toMarkerPos) {
732 r2 = GetCharWindowLocation (FindPreviousCharacter (toMarkerPos));
735 size_t realEndOfRowOfFromMarkerPos = GetRealEndOfRowContainingPosition (fromMarkerPos);
737 Led_Rect boundingRect;
738 boundingRect.top = r1.GetTop ();
739 boundingRect.bottom = r2.GetBottom ();
741 if (realEndOfRowOfFromMarkerPos >= toMarkerPos) {
758 size_t startOfRow = GetStartOfRowContainingPosition (fromMarkerPos);
759 size_t endOfRow = GetEndOfRowContainingPosition (fromMarkerPos);
760 bool segmentHilightedAtStart = (fromMarkerPos == startOfRow);
761 bool segmentHilightedAtEnd = endOfRow < toMarkerPos;
763 boundingRect.left = min (r1.GetLeft (), r2.GetLeft ());
764 boundingRect.right = max (r1.GetRight (), r2.GetRight ());
774 TextLayoutBlock_Copy text = GetTextLayoutBlock (startOfRow, endOfRow);
776 using ScriptRunElt = TextLayoutBlock::ScriptRunElt;
777 vector<ScriptRunElt> runs = text.GetScriptRuns ();
778 if (runs.size () > 1) {
780 for (
auto i = runs.begin (); i != runs.end (); ++i) {
781 const ScriptRunElt& se = *i;
782 if (TextStore::Overlap (startOfRow + se.fRealStart, startOfRow + se.fRealEnd, fromMarkerPos, toMarkerPos)) {
789 if (fromMarkerPos <= startOfRow + se.fRealStart and startOfRow + se.fRealStart <= toMarkerPos) {
790 Led_Rect t1 = GetCharWindowLocation (startOfRow + se.fRealStart);
791 boundingRect.left = min (boundingRect.left, t1.GetLeft ());
792 boundingRect.right = max (boundingRect.right, t1.GetRight ());
795 if (fromMarkerPos <= startOfRow + se.fRealEnd and startOfRow + se.fRealEnd <= toMarkerPos) {
796 Led_Rect t2 = GetCharWindowLocation (FindPreviousCharacter (startOfRow + se.fRealEnd));
797 boundingRect.left = min (boundingRect.left, t2.GetLeft ());
798 boundingRect.right = max (boundingRect.right, t2.GetRight ());
804 if (segmentHilightedAtStart) {
805 boundingRect.left = windowRect.left;
807 if (segmentHilightedAtEnd) {
808 boundingRect.right = windowRect.right;
815 boundingRect.left = windowRect.left;
816 boundingRect.right = windowRect.right;
820 boundingRect.left = max (boundingRect.left, windowRect.left);
821 boundingRect.right = min (boundingRect.right, windowRect.right);
823 Ensure (boundingRect.right >= boundingRect.left);
824 return (boundingRect);
841Led_Rect TextImager::GetIntraRowTextWindowBoundingRect (
size_t fromMarkerPos,
size_t toMarkerPos)
const
843 Require (fromMarkerPos <= toMarkerPos);
845 Led_Rect windowRect = GetWindowRect ();
857 Led_Rect r1 = GetCharWindowLocation (fromMarkerPos);
859 if (fromMarkerPos != toMarkerPos) {
860 r2 = GetCharWindowLocation (FindPreviousCharacter (toMarkerPos));
863 Led_Rect boundingRect = r1;
865 if (GetTextDirection (fromMarkerPos) == eLeftToRight) {
866 boundingRect.left = r1.GetLeft ();
867 boundingRect.right = (fromMarkerPos == toMarkerPos) ? boundingRect.left : r2.GetRight ();
870 boundingRect.right = r1.GetRight ();
871 boundingRect.left = (fromMarkerPos == toMarkerPos) ? boundingRect.right : r2.GetLeft ();
873 Ensure (boundingRect.right >= boundingRect.left);
875 return (boundingRect);
888vector<Led_Rect> TextImager::GetRowHilightRects (
const TextLayoutBlock& text,
size_t rowStart,
size_t rowEnd,
size_t hilightStart,
size_t hilightEnd)
const
890 Require (rowEnd == GetEndOfRowContainingPosition (rowStart));
892 vector<Led_Rect> result;
894 size_t realEndOfRow = GetRealEndOfRowContainingPosition (rowStart);
895 bool segmentHilighted = max (rowStart, hilightStart) < min (realEndOfRow, hilightEnd);
896 if (segmentHilighted) {
897 bool segmentHilightedAtStart =
false;
898 bool segmentHilightedAtEnd =
false;
899 if (GetUseSelectEOLBOLRowHilightStyle ()) {
900 segmentHilightedAtStart = (hilightStart < rowStart) or (hilightStart == 0);
901 segmentHilightedAtEnd = rowEnd < hilightEnd;
902 if (segmentHilightedAtEnd and rowEnd >= GetEnd ()) {
903 segmentHilightedAtEnd =
false;
907 hilightStart = max (hilightStart, rowStart);
909 using ScriptRunElt = TextLayoutBlock::ScriptRunElt;
910 vector<ScriptRunElt> runs = text.GetScriptRuns ();
912 for (
auto i = runs.begin (); i != runs.end (); ++i) {
913 const ScriptRunElt& se = *i;
914 size_t hRunStart = max (se.fRealStart + rowStart, hilightStart);
915 size_t hRunEnd = min (se.fRealEnd + rowStart, hilightEnd);
916 if (hRunStart < hRunEnd) {
917 Led_Rect hilightRect = GetIntraRowTextWindowBoundingRect (hRunStart, hRunEnd);
918 Assert (hilightRect.GetWidth () >= 0);
919 Assert (hilightRect.GetHeight () >= 0);
920 if (not hilightRect.IsEmpty ()) {
921 result.push_back (hilightRect);
927 if (segmentHilightedAtStart) {
928 size_t realOffsetOfVirtualRowStart = rowStart;
929 if (text.GetTextLength () != 0) {
930 realOffsetOfVirtualRowStart += text.MapVirtualOffsetToReal (0);
933 Led_Rect hilightRect = GetCharWindowLocation (realOffsetOfVirtualRowStart);
934 hilightRect.right = hilightRect.left;
935 hilightRect.left = min (GetWindowRect ().GetLeft (), hilightRect.left);
936 Assert (hilightRect.GetWidth () >= 0);
937 Assert (hilightRect.GetHeight () >= 0);
938 if (not hilightRect.IsEmpty ()) {
939 result.push_back (hilightRect);
942 if (segmentHilightedAtEnd) {
943 size_t realOffsetOfVirtualRowEnd = rowStart;
944 if (text.GetTextLength () != 0) {
945 realOffsetOfVirtualRowEnd += text.MapVirtualOffsetToReal (text.GetTextLength () - 1);
948 Led_Rect hilightRect = GetCharWindowLocation (realOffsetOfVirtualRowEnd);
949 hilightRect.left = hilightRect.GetRight ();
950 hilightRect.right = max (hilightRect.right, GetWindowRect ().GetRight ());
951 Assert (hilightRect.GetWidth () >= 0);
952 Assert (hilightRect.GetHeight () >= 0);
953 if (not hilightRect.IsEmpty ()) {
954 result.push_back (hilightRect);
961 for (
auto orit = result.begin (); orit != result.end (); ++orit) {
962 Ensure ((*orit).GetWidth () > 0);
963 Ensure ((*orit).GetHeight () > 0);
964 for (
auto irit = orit + 1; irit != result.end (); ++irit) {
966 Ensure (hr.GetWidth () > 0);
967 Ensure (hr.GetHeight () > 0);
968 Ensure (not Intersect (hr, *orit));
981TextLayoutBlock_Copy TextImager::GetTextLayoutBlock (
size_t rowStart,
size_t rowEnd)
const
983 size_t rowLen = rowEnd - rowStart;
985 CopyOut (rowStart, rowLen, rowBuf.data ());
986 TextLayoutBlock_Basic text{rowBuf.data (), rowBuf.data () + rowLen};
987 return TextLayoutBlock_Copy (text);
1001vector<Led_Rect> TextImager::GetSelectionWindowRects (
size_t from,
size_t to)
const
1003 Require (from <= to);
1005 vector<Led_Rect> result;
1007 from = max (from, GetMarkerPositionOfStartOfWindow ());
1008 to = min (to, FindNextCharacter (GetMarkerPositionOfEndOfWindow ()));
1015 size_t topRow = GetRowContainingPosition (from);
1016 size_t bottomRow = GetRowContainingPosition (to);
1017 Assert (topRow <= bottomRow);
1021 if (GetStartOfRow (bottomRow) == to) {
1023 Assert (topRow < bottomRow);
1027 CoordinateType lastRowBottom = 0;
1028 for (
size_t curRow = topRow;;) {
1029 size_t firstCharInRow = from;
1030 if (topRow != curRow) {
1031 firstCharInRow = GetStartOfRow (curRow);
1034 size_t startOfRow = GetStartOfRowContainingPosition (firstCharInRow);
1035 size_t endOfRow = GetEndOfRowContainingPosition (startOfRow);
1037 TextLayoutBlock_Copy text = GetTextLayoutBlock (startOfRow, endOfRow);
1039 size_t rowLen = endOfRow - startOfRow;
1041 CopyOut (startOfRow, rowLen, rowBuf);
1042 TextLayoutBlock_Basic text{rowBuf, rowBuf + rowLen};
1045 vector<Led_Rect> hilightRects = GetRowHilightRects (text, startOfRow, endOfRow, GetSelectionStart (), GetSelectionEnd ());
1046 CoordinateType newMinTop = lastRowBottom;
1047 CoordinateType newMaxBottom = lastRowBottom;
1048 for (
auto i = hilightRects.begin (); i != hilightRects.end (); ++i) {
1049 Led_Rect hilightRect = *i;
1050 Require (hilightRect.GetWidth () >= 0);
1051 Assert (hilightRect.GetHeight () > 0);
1052 if (not hilightRect.IsEmpty ()) {
1053 result.push_back (hilightRect);
1055 newMinTop = min (newMinTop, hilightRect.top);
1056 newMaxBottom = max (newMaxBottom, hilightRect.bottom);
1064 if (lastRowBottom < newMinTop) {
1067 Led_Rect hScrollAdjustedWR = GetWindowRect () - Led_Point (0, GetHScrollPos ());
1068 result.push_back (Led_Rect (lastRowBottom, hScrollAdjustedWR.GetLeft (), newMinTop - lastRowBottom, hScrollAdjustedWR.GetWidth ()));
1070 lastRowBottom = newMaxBottom;
1072 if (curRow == bottomRow) {
1080 for (
auto orit = result.begin (); orit != result.end (); ++orit) {
1081 Ensure ((*orit).GetWidth () > 0);
1082 Ensure ((*orit).GetHeight () > 0);
1083 for (
auto irit = orit + 1; irit != result.end (); ++irit) {
1084 Led_Rect hr = *irit;
1085 Ensure (hr.GetWidth () > 0);
1086 Ensure (hr.GetHeight () > 0);
1087 Ensure (not Intersect (hr, *orit));
1103void TextImager::GetSelectionWindowRegion (Region* r,
size_t from,
size_t to)
const
1106 vector<Led_Rect> selRects = GetSelectionWindowRects (from, to);
1107 for (
auto i = selRects.begin (); i != selRects.end (); ++i) {
1108 AddRectangleToRegion (*i, r);
1129void TextImager::EraseBackground (Tablet* tablet,
const Led_Rect& subsetToDraw,
bool printing)
1134 tablet->EraseBackground_SolidHelper (subsetToDraw, GetEffectiveDefaultTextColor (TextImager::eDefaultBackgroundColor));
1145void TextImager::HilightArea (Tablet* tablet, Led_Rect hiliteArea)
1148 tablet->HilightArea_SolidHelper (hiliteArea, GetEffectiveDefaultTextColor (eDefaultSelectedTextBackgroundColor),
1149 GetEffectiveDefaultTextColor (eDefaultSelectedTextColor),
1150 GetEffectiveDefaultTextColor (eDefaultBackgroundColor), GetEffectiveDefaultTextColor (eDefaultTextColor));
1160void TextImager::HilightArea (Tablet* tablet,
const Region& hiliteArea)
1163 tablet->HilightArea_SolidHelper (hiliteArea, GetEffectiveDefaultTextColor (eDefaultSelectedTextBackgroundColor),
1164 GetEffectiveDefaultTextColor (eDefaultSelectedTextColor),
1165 GetEffectiveDefaultTextColor (eDefaultBackgroundColor), GetEffectiveDefaultTextColor (eDefaultTextColor));
1179void TextImager::DrawRow (Tablet* tablet,
const Led_Rect& currentRowRect,
const Led_Rect& invalidRowRect,
const TextLayoutBlock& text,
1180 size_t rowStart,
size_t rowEnd,
bool printing)
1183 Require (rowEnd == GetEndOfRowContainingPosition (rowStart));
1193 EraseBackground (tablet, currentRowRect, printing);
1195 DrawRowSegments (tablet, currentRowRect, invalidRowRect, text, rowStart, rowEnd);
1201 DrawRowHilight (tablet, currentRowRect, invalidRowRect, text, rowStart, rowEnd);
1214void TextImager::DrawRowSegments (Tablet* tablet,
const Led_Rect& currentRowRect,
const Led_Rect& invalidRowRect,
1215 const TextLayoutBlock& text,
size_t rowStart,
size_t rowEnd)
1219#if qStroika_Foundation_Debug_AssertionsChecked && 0
1222 size_t startOfRow = GetStartOfRow (row);
1223 size_t endOfRow = GetEndOfRow (row);
1224 size_t realEndOfRow = GetRealEndOfRow (row);
1225 Assert (startOfRow == start);
1226 Assert (endOfRow <= end);
1227 Assert (end <= realEndOfRow);
1241 size_t segEnd = rowEnd;
1242 CoordinateType baseLine = currentRowRect.top + MeasureSegmentBaseLine (rowStart, segEnd);
1248 baseLine = min (baseLine, currentRowRect.bottom);
1250 DrawSegment (tablet, rowStart, segEnd, text, currentRowRect, invalidRowRect, baseLine,
nullptr);
1260void TextImager::DrawRowHilight (Tablet* tablet, [[maybe_unused]]
const Led_Rect& currentRowRect,
const Led_Rect& ,
1261 const TextLayoutBlock& text,
size_t rowStart,
size_t rowEnd)
1263 Require (rowEnd == GetEndOfRowContainingPosition (rowStart));
1265 if (GetSelectionShown ()) {
1266 vector<Led_Rect> hilightRects = GetRowHilightRects (text, rowStart, rowEnd, GetSelectionStart (), GetSelectionEnd ());
1267 for (
auto i = hilightRects.begin (); i != hilightRects.end (); ++i) {
1268 Led_Rect hilightRect = *i;
1271 if (Intersect (hilightRect, currentRowRect) or hilightRect.IsEmpty ()) {
1272 Led_Rect x = hilightRect;
1273 Led_Rect y = currentRowRect;
1276 Assert (Intersect (x, y) or x.IsEmpty ());
1279 HilightArea (tablet, hilightRect);
1291void TextImager::DrawInterLineSpace (DistanceType interlineSpace, Tablet* tablet, CoordinateType vPosOfTopOfInterlineSpace,
1292 bool segmentHilighted,
bool printing)
1298 if (interlineSpace != 0) {
1299 Led_Rect fillRect = GetWindowRect ();
1300 fillRect.top = vPosOfTopOfInterlineSpace;
1301 fillRect.bottom = vPosOfTopOfInterlineSpace + interlineSpace;
1302 EraseBackground (tablet, fillRect, printing);
1303 if (segmentHilighted) {
1304 HilightArea (tablet, fillRect);
1325bool TextImager::ContainsMappedDisplayCharacters (
const Led_tChar* ,
size_t )
const
1339void TextImager::ReplaceMappedDisplayCharacters (
const Led_tChar* srcText, Led_tChar* copyText,
size_t nTChars)
const
1342 (void)::memcpy (copyText, srcText, nTChars *
sizeof (Led_tChar));
1355size_t TextImager::RemoveMappedDisplayCharacters (Led_tChar* ,
size_t nTChars)
const
1372void TextImager::PatchWidthRemoveMappedDisplayCharacters (
const Led_tChar* , DistanceType* ,
size_t )
const
1381bool TextImager::ContainsMappedDisplayCharacters_HelperForChar (
const Led_tChar* text,
size_t nTChars, Led_tChar charToMap)
1384 const Led_tChar* end = &text[nTChars];
1385 for (
const Led_tChar* cur = text; cur < end; cur = Led_NextChar (cur)) {
1386 if (*cur == charToMap) {
1399void TextImager::ReplaceMappedDisplayCharacters_HelperForChar (Led_tChar* copyText,
size_t nTChars, Led_tChar charToMap, Led_tChar charToMapTo)
1402 Led_tChar* end = ©Text[nTChars];
1403 for (Led_tChar* cur = copyText; cur < end; cur = Led_NextChar (cur)) {
1404 if (*cur == charToMap) {
1416size_t TextImager::RemoveMappedDisplayCharacters_HelperForChar (Led_tChar* copyText,
size_t nTChars, Led_tChar charToRemove)
1419 Led_tChar* outPtr = copyText;
1420 Led_tChar* end = copyText + nTChars;
1421 for (
const Led_tChar* cur = copyText; cur < end; cur = Led_NextChar (cur)) {
1422 if (*cur != charToRemove) {
1424 outPtr = Led_NextChar (outPtr);
1427 size_t newLen = outPtr - copyText;
1428 Assert (newLen <= nTChars);
1438void TextImager::PatchWidthRemoveMappedDisplayCharacters_HelperForChar (
const Led_tChar* srcText, DistanceType* distanceResults,
1439 size_t nTChars, Led_tChar charToRemove)
1443 DistanceType cumSubtract = 0;
1444 const Led_tChar* end = srcText + nTChars;
1445 for (
const Led_tChar* cur = srcText; cur < end; cur = Led_NextChar (cur)) {
1446 size_t i = cur - srcText;
1447 Assert (i < nTChars);
1448 if (*cur == charToRemove) {
1449 DistanceType thisSoftBreakWidth = i == 0 ? distanceResults[0] : (distanceResults[i] - distanceResults[i - 1]);
1450 cumSubtract = thisSoftBreakWidth;
1452 distanceResults[i] -= cumSubtract;
1469void TextImager::DrawSegment (Tablet* tablet,
size_t from,
size_t to,
const TextLayoutBlock& text,
const Led_Rect& drawInto,
1470 const Led_Rect& , CoordinateType useBaseLine, DistanceType* pixelsDrawn)
1472 DrawSegment_ (tablet, GetDefaultFont (), from, to, text, drawInto, useBaseLine, pixelsDrawn);
1484void TextImager::DrawSegment_ (Tablet* tablet,
const FontSpecification& fontSpec,
size_t from,
size_t to,
const TextLayoutBlock& text,
1485 const Led_Rect& drawInto, CoordinateType useBaseLine, DistanceType* pixelsDrawn)
const
1488 Assert (from <= to);
1498 [[maybe_unused]]
size_t length = to - from;
1503 Color foreColor = fontSpec.GetTextColor ();
1504 Color backColor = GetEffectiveDefaultTextColor (eDefaultBackgroundColor);
1505 tablet->SetBackColor (backColor);
1506 tablet->SetForeColor (foreColor);
1508 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1510 DistanceType ascent = fCachedFontInfo.GetAscent ();
1511 Assert (useBaseLine >= drawInto.top);
1514 CoordinateType drawCharTop = useBaseLine - ascent;
1517 if (fontSpec.GetStyle_SubOrSuperScript () == FontSpecification::eSuperscript) {
1523 drawCharTop = useBaseLine - ascent * 3 / 2;
1525 else if (fontSpec.GetStyle_SubOrSuperScript () == FontSpecification::eSubscript) {
1526 drawCharTop += fCachedFontInfo.GetDescent ();
1529 using ScriptRunElt = TextLayoutBlock::ScriptRunElt;
1530 vector<ScriptRunElt> runs = text.GetScriptRuns ();
1531 Assert (not runs.empty () or (length == 0));
1532 if (runs.size () > 1) {
1534 sort (runs.begin (), runs.end (), TextLayoutBlock::LessThanVirtualStart ());
1537 const Led_tChar* fullVirtualText = text.PeekAtVirtualText ();
1538 Led_Point outputAt = Led_Point (drawCharTop, drawInto.left);
1539 if (pixelsDrawn !=
nullptr) {
1542 for (
auto i = runs.begin (); i != runs.end (); ++i) {
1543 const ScriptRunElt& se = *i;
1544 size_t runLength = se.fVirtualEnd - se.fVirtualStart;
1550 (void)::memcpy (
static_cast<Led_tChar*
> (useVirtualText), &fullVirtualText[se.fVirtualStart], runLength *
sizeof (Led_tChar));
1555 Led_tChar* drawText = useVirtualText.
data ();
1556 size_t drawTextLen = runLength;
1558 if (ContainsMappedDisplayCharacters (drawText, drawTextLen)) {
1560 ReplaceMappedDisplayCharacters (drawText, mappedDisplayBuf.data (), drawTextLen);
1561 size_t newLength = RemoveMappedDisplayCharacters (mappedDisplayBuf.data (), drawTextLen);
1562 Assert (newLength <= drawTextLen);
1563 drawText = mappedDisplayBuf.data ();
1564 drawTextLen = newLength;
1570 DistanceType amountDrawn = 0;
1571 tablet->TabbedTextOut (fCachedFontInfo, drawText, drawTextLen, se.fDirection, outputAt, GetWindowRect ().left,
1572 GetTabStopList (from), &amountDrawn, GetHScrollPos ());
1573 outputAt.h += amountDrawn;
1574 if (pixelsDrawn !=
nullptr) {
1575 *pixelsDrawn += amountDrawn;
1580void TextImager::MeasureSegmentWidth (
size_t from,
size_t to,
const Led_tChar* text, DistanceType* distanceResults)
const
1582 MeasureSegmentWidth_ (GetDefaultFont (), from, to, text, distanceResults);
1592void TextImager::MeasureSegmentWidth_ (
const FontSpecification& fontSpec,
size_t from,
size_t to,
const Led_tChar* text, DistanceType* distanceResults)
const
1594 Require (to > from);
1596 Tablet_Acquirer tablet (
this);
1598 size_t length = to - from;
1599 Assert (length > 0);
1601 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1603 if (ContainsMappedDisplayCharacters (text, length)) {
1605 ReplaceMappedDisplayCharacters (text, buf2.data (), length);
1606 tablet->MeasureText (fCachedFontInfo, buf2.data (), length, distanceResults);
1607 PatchWidthRemoveMappedDisplayCharacters (buf2.data (), distanceResults, length);
1610 tablet->MeasureText (fCachedFontInfo, text, length, distanceResults);
1614DistanceType TextImager::MeasureSegmentHeight (
size_t from,
size_t to)
const
1616 return (MeasureSegmentHeight_ (GetDefaultFont (), from, to));
1619DistanceType TextImager::MeasureSegmentHeight_ (
const FontSpecification& fontSpec,
size_t ,
size_t )
const
1621 Tablet_Acquirer tablet (
this);
1623 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1624 return (fCachedFontInfo.GetLineHeight ());
1627DistanceType TextImager::MeasureSegmentBaseLine (
size_t from,
size_t to)
const
1629 return (MeasureSegmentBaseLine_ (GetDefaultFont (), from, to));
1632DistanceType TextImager::MeasureSegmentBaseLine_ (
const FontSpecification& fontSpec,
size_t ,
size_t )
const
1634 Tablet_Acquirer tablet (
this);
1636 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1637 return fCachedFontInfo.GetAscent ();
1640FontMetrics TextImager::GetFontMetricsAt ([[maybe_unused]]
size_t charAfterPos)
const
1642 Tablet_Acquirer tablet (
this);
1647 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1648 return fCachedFontInfo;
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define RequireNotNull(p)
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
nonvirtual pointer data() noexcept
returns a (possibly const) pointer to the start of the live buffer data. This return value can be inv...
nonvirtual void GrowToSize(size_t nElements)
conditional_t< qStroika_Foundation_Characters_SDKUseswchar_t, wchar_t, char > SDKChar