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_Foundation_Common_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_Foundation_Common_Platform_Windows
50 , fRestoreObject{nullptr}
51 , fRestoreAttribObject{nullptr}
54#if qStroika_Foundation_Common_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 ();
97#elif qStroika_FeatureSupported_XWindows
98 tablet->SetFont (fontSpec);
99 imager->fCachedFontInfo = tablet->GetFontMetrics ();
108TextImager::TextImager ()
109 : fTextStore{nullptr}
110 , fDefaultFont{GetStaticDefaultFont ()}
111 , fForceAllRowsShowing{true}
112 , fImageUsingOffscreenBitmaps{qUseOffscreenBitmapsToReduceFlicker}
114 , fSuppressGoalColumnRecompute{false}
115 , fSelectionGoalColumn{TWIPS{0}}
116 , fUseEOLBOLRowHilightStyle{true}
117 , fSelectionShown{false}
118 , fWindowRect{Led_Rect{0, 0, 0, 0}}
119 , fHiliteMarker{nullptr}
120 , fWeAllocedHiliteMarker{false}
124#if qStroika_Foundation_Common_Platform_Windows
127 fCachedFontValid{false}
130 for (
Color** i = &fDefaultColorIndex[0]; i < &fDefaultColorIndex[eMaxDefaultColorIndex]; ++i) {
135TextImager::~TextImager ()
137 Require (fTextStore ==
nullptr);
138 Require (fHiliteMarker ==
nullptr);
139 for (
Color** i = &fDefaultColorIndex[0]; i < &fDefaultColorIndex[eMaxDefaultColorIndex]; ++i) {
143#if qStroika_Foundation_Common_Platform_Windows
148TextStore* TextImager::PeekAtTextStore ()
const
160void TextImager::SpecifyTextStore (TextStore* useTextStore)
162 if (fTextStore != useTextStore) {
163 if (fTextStore !=
nullptr) {
164 HookLosingTextStore ();
166 fTextStore = useTextStore;
167 if (fTextStore !=
nullptr) {
168 HookGainedNewTextStore ();
173void TextImager::HookLosingTextStore ()
175 HookLosingTextStore_ ();
178void TextImager::HookLosingTextStore_ ()
182 if (fHiliteMarker !=
nullptr) {
183 Assert (fWeAllocedHiliteMarker);
184 GetTextStore ().RemoveMarker (fHiliteMarker);
185 delete fHiliteMarker;
186 fHiliteMarker =
nullptr;
187 fTextStore->RemoveMarkerOwner (
this);
191void TextImager::HookGainedNewTextStore ()
193 HookGainedNewTextStore_ ();
196void TextImager::HookGainedNewTextStore_ ()
199 if (fHiliteMarker ==
nullptr) {
200 fTextStore->AddMarkerOwner (
this);
201 SetHilightMarker (
nullptr);
212void TextImager::PurgeUnneededMemory ()
214 InvalidateAllCaches ();
224void TextImager::InvalidateAllCaches ()
227#if qStroika_Foundation_Common_Platform_Windows
229 fCachedFont =
nullptr;
231 fCachedFontValid =
false;
246void TextImager::SetDefaultFont (
const IncrementalFontSpecification& defaultFont)
248 SetDefaultFont_ (defaultFont);
251void TextImager::SetDefaultFont_ (
const IncrementalFontSpecification& defaultFont)
253 if (PeekAtTextStore () !=
nullptr) {
254 TextStore::SimpleUpdater u (GetTextStore (), 0, GetTextStore ().GetEnd (),
false);
255 fDefaultFont.MergeIn (defaultFont);
256 if (defaultFont.GetTextColor_Valid ()) {
257 SetDefaultTextColor (eDefaultTextColor, defaultFont.GetTextColor ());
261 fDefaultFont.MergeIn (defaultFont);
262 if (defaultFont.GetTextColor_Valid ()) {
263 SetDefaultTextColor (eDefaultTextColor, defaultFont.GetTextColor ());
284 return GetDefaultFont ();
287void TextImager::SetSelectionShown (
bool shown)
289 fSelectionShown = shown;
300const TabStopList& TextImager::GetTabStopList (
size_t )
const
303 static SimpleTabStopList sDefaultTabStopList = SimpleTabStopList (TWIPS (1440 / 3));
304 return sDefaultTabStopList;
311void TextImager::SetWindowRect (
const Led_Rect& windowRect)
313 SetWindowRect_ (windowRect);
321void TextImager::ScrollSoShowingHHelper (
size_t markerPos,
size_t andTryToShowMarkerPos)
323 CoordinateType maxHScrollPos = ComputeMaxHScrollPos ();
324 CoordinateType hsp = GetHScrollPos ();
329 if (maxHScrollPos == 0) {
336 Led_Rect windowRect = GetWindowRect ();
340 Led_Rect andTryRRR = GetCharWindowLocation (andTryToShowMarkerPos);
341 CoordinateType whereAtInGlobalCoords = windowRect.left - andTryRRR.left;
342 if (andTryRRR.left < windowRect.left) {
343 Assert (hsp >= whereAtInGlobalCoords);
344 hsp -= whereAtInGlobalCoords;
346 else if (andTryRRR.right > windowRect.right) {
347 CoordinateType howFarOffRight = andTryRRR.right - windowRect.right;
348 hsp += howFarOffRight;
349 hsp = min (hsp, maxHScrollPos);
353 Assert (hsp <= maxHScrollPos);
357 Led_Rect rrr = GetCharWindowLocation (markerPos);
360 CoordinateType adjustRRRBy = GetHScrollPos () - hsp;
361 rrr += Led_Point (0, adjustRRRBy);
364 CoordinateType whereAtInGlobalCoords = windowRect.GetLeft () - rrr.GetLeft ();
365 if (rrr.GetLeft () < windowRect.GetLeft ()) {
366 Assert (hsp >= whereAtInGlobalCoords);
367 hsp -= whereAtInGlobalCoords;
369 else if (rrr.GetRight () > windowRect.GetRight ()) {
370 CoordinateType howFarOffRight = rrr.GetRight () - windowRect.GetRight ();
371 hsp += howFarOffRight;
372 hsp = min (hsp, maxHScrollPos);
377 Assert (hsp <= maxHScrollPos);
381void TextImager::SetHScrollPos (CoordinateType hScrollPos)
383 if (hScrollPos != GetHScrollPos ()) {
384 SetHScrollPos_ (hScrollPos);
385 InvalidateScrollBarParameters ();
406DistanceType TextImager::ComputeMaxHScrollPos ()
const
416DistanceType TextImager::CalculateLongestRowInWindowPixelWidth ()
const
418 size_t startOfWindow = GetMarkerPositionOfStartOfWindow ();
419 size_t endOfWindow = GetMarkerPositionOfEndOfWindow ();
421 DistanceType longestRowWidth = 0;
422 for (
size_t curOffset = startOfWindow; curOffset < endOfWindow;) {
423 DistanceType thisRowWidth = CalcSegmentSize (curOffset, GetEndOfRowContainingPosition (curOffset));
424 longestRowWidth = max (longestRowWidth, thisRowWidth);
426 size_t newOffset = GetStartOfNextRowFromRowContainingPosition (curOffset);
427 if (newOffset <= curOffset) {
430 curOffset = newOffset;
433 return longestRowWidth;
442void TextImager::TabletChangedMetrics ()
444 InvalidateAllCaches ();
447void TextImager::SetSelection (
size_t start,
size_t end)
450 Assert (end <= GetEnd ());
451 if (start != GetSelectionStart () or end != GetSelectionEnd ()) {
452 SetSelection_ (start, end);
456size_t TextImager::GetSelectionStart ()
const
459 return (fHiliteMarker->GetStart ());
462size_t TextImager::GetSelectionEnd ()
const
465 return (fHiliteMarker->GetEnd ());
468void TextImager::GetSelection (
size_t* start,
size_t* end)
const
473 fHiliteMarker->GetRange (start, end);
476void TextImager::SetSelection_ (
size_t start,
size_t end)
478 Require (start >= 0);
479 Require (end <= GetEnd ());
480 Require (start <= end);
481 GetTextStore ().SetMarkerRange (fHiliteMarker, start, end);
484void TextImager::SetHilightMarker (HilightMarker* newHilightMarker)
488 if (fHiliteMarker !=
nullptr) {
489 fHiliteMarker->GetRange (&start, &end);
490 GetTextStore ().RemoveMarker (fHiliteMarker);
491 if (fWeAllocedHiliteMarker) {
492 delete fHiliteMarker;
495 fHiliteMarker = newHilightMarker;
496 fWeAllocedHiliteMarker = bool (fHiliteMarker ==
nullptr);
497 if (fHiliteMarker ==
nullptr) {
498 fHiliteMarker =
new HilightMarker ();
501 GetTextStore ().AddMarker (fHiliteMarker, start, end - start,
this);
508void TextImager::RecomputeSelectionGoalColumn ()
510 if (not fSuppressGoalColumnRecompute) {
514 DistanceType lhs = 0;
515 DistanceType rhs = 0;
516 GetRowRelativeCharLoc (GetSelectionStart (), &lhs, &rhs);
517 SetSelectionGoalColumn (Tablet_Acquirer (
this)->CvtToTWIPSH (lhs + (rhs - lhs) / 2));
525size_t TextImager::ComputeRelativePosition (
size_t fromPos, CursorMovementDirection direction, CursorMovementUnit movementUnit)
533 switch (movementUnit) {
534 case eCursorByChar: {
535 return (FindPreviousCharacter (fromPos));
538 case eCursorByWord: {
539 return (GetTextStore ().FindFirstWordStartStrictlyBeforePosition (fromPos));
543 size_t startOfStartRow = GetStartOfRowContainingPosition (fromPos);
544 size_t startOfPrevRow = GetStartOfPrevRowFromRowContainingPosition (fromPos);
545 if (startOfStartRow == startOfPrevRow) {
550 return GetRowRelativeCharAtLoc (Tablet_Acquirer (
this)->CvtFromTWIPSH (GetSelectionGoalColumn ()), startOfPrevRow);
554 case eCursorByLine: {
555 size_t fromLine = GetTextStore ().GetLineContainingPosition (fromPos);
556 size_t newLine = (fromLine > 0) ? fromLine - 1 : 0;
557 if (newLine == fromLine) {
563 size_t positionInLine = fromPos - GetTextStore ().GetStartOfLine (fromLine);
564 Assert (positionInLine <= GetTextStore ().GetLineLength (fromLine));
565 positionInLine = min (positionInLine, GetTextStore ().GetLineLength (newLine));
566 return GetTextStore ().GetStartOfLine (newLine) + positionInLine;
570 case eCursorByWindow: {
574 case eCursorByBuffer: {
583 case eCursorForward: {
584 switch (movementUnit) {
585 case eCursorByChar: {
586 return (FindNextCharacter (fromPos));
589 case eCursorByWord: {
590 TextStore& ts = GetTextStore ();
591 return (ts.FindFirstWordStartAfterPosition (ts.FindNextCharacter (fromPos)));
595 size_t startOfStartRow = GetStartOfRowContainingPosition (fromPos);
596 size_t startOfNextRow = GetStartOfNextRowFromRowContainingPosition (fromPos);
597 if (startOfStartRow == startOfNextRow) {
602 return GetRowRelativeCharAtLoc (Tablet_Acquirer (
this)->CvtFromTWIPSH (GetSelectionGoalColumn ()), startOfNextRow);
606 case eCursorByLine: {
607 size_t fromLine = GetTextStore ().GetLineContainingPosition (fromPos);
608 size_t newLine = (fromLine == GetTextStore ().GetLineCount () - 1) ? fromLine : (fromLine + 1);
609 Assert (newLine <= GetTextStore ().GetLineCount () - 1);
610 if (newLine == fromLine) {
616 size_t positionInLine = fromPos - GetTextStore ().GetStartOfLine (fromLine);
617 Assert (positionInLine <= GetTextStore ().GetLineLength (fromLine));
618 positionInLine = min (positionInLine, GetTextStore ().GetLineLength (newLine));
619 return (GetTextStore ().GetStartOfLine (newLine) + positionInLine);
623 case eCursorByWindow: {
627 case eCursorByBuffer: {
636 case eCursorToStart: {
637 switch (movementUnit) {
638 case eCursorByChar: {
642 case eCursorByWord: {
643 return (GetTextStore ().FindFirstWordStartStrictlyBeforePosition (FindNextCharacter (fromPos)));
647 size_t fromRow = GetRowContainingPosition (fromPos);
648 return (GetStartOfRow (fromRow));
651 case eCursorByLine: {
652 size_t result = GetTextStore ().GetStartOfLineContainingPosition (fromPos);
653 if (fromPos == result) {
654 result = GetTextStore ().GetStartOfLineContainingPosition (GetTextStore ().FindPreviousCharacter (result));
659 case eCursorByWindow: {
660 return (GetMarkerPositionOfStartOfWindow ());
663 case eCursorByBuffer: {
673 switch (movementUnit) {
674 case eCursorByChar: {
678 case eCursorByWord: {
679 return (GetTextStore ().FindFirstWordEndAfterPosition (fromPos));
683 size_t fromRow = GetRowContainingPosition (fromPos);
684 return (GetEndOfRow (fromRow));
687 case eCursorByLine: {
688 size_t result = GetTextStore ().GetEndOfLineContainingPosition (fromPos);
689 if (fromPos == result) {
690 result = GetTextStore ().GetEndOfLineContainingPosition (GetTextStore ().FindNextCharacter (result));
695 case eCursorByWindow: {
696 return (GetMarkerPositionOfEndOfWindow ());
699 case eCursorByBuffer: {
700 return (GetTextStore ().GetEnd ());
727Led_Rect TextImager::GetTextWindowBoundingRect (
size_t fromMarkerPos,
size_t toMarkerPos)
const
729 Require (fromMarkerPos <= toMarkerPos);
731 Led_Rect windowRect = GetWindowRect ();
732 Led_Rect r1 = GetCharWindowLocation (fromMarkerPos);
734 if (fromMarkerPos != toMarkerPos) {
735 r2 = GetCharWindowLocation (FindPreviousCharacter (toMarkerPos));
738 size_t realEndOfRowOfFromMarkerPos = GetRealEndOfRowContainingPosition (fromMarkerPos);
740 Led_Rect boundingRect;
741 boundingRect.top = r1.GetTop ();
742 boundingRect.bottom = r2.GetBottom ();
744 if (realEndOfRowOfFromMarkerPos >= toMarkerPos) {
761 size_t startOfRow = GetStartOfRowContainingPosition (fromMarkerPos);
762 size_t endOfRow = GetEndOfRowContainingPosition (fromMarkerPos);
763 bool segmentHilightedAtStart = (fromMarkerPos == startOfRow);
764 bool segmentHilightedAtEnd = endOfRow < toMarkerPos;
766 boundingRect.left = min (r1.GetLeft (), r2.GetLeft ());
767 boundingRect.right = max (r1.GetRight (), r2.GetRight ());
777 TextLayoutBlock_Copy text = GetTextLayoutBlock (startOfRow, endOfRow);
779 using ScriptRunElt = TextLayoutBlock::ScriptRunElt;
780 vector<ScriptRunElt> runs = text.GetScriptRuns ();
781 if (runs.size () > 1) {
783 for (
auto i = runs.begin (); i != runs.end (); ++i) {
784 const ScriptRunElt& se = *i;
785 if (TextStore::Overlap (startOfRow + se.fRealStart, startOfRow + se.fRealEnd, fromMarkerPos, toMarkerPos)) {
792 if (fromMarkerPos <= startOfRow + se.fRealStart and startOfRow + se.fRealStart <= toMarkerPos) {
793 Led_Rect t1 = GetCharWindowLocation (startOfRow + se.fRealStart);
794 boundingRect.left = min (boundingRect.left, t1.GetLeft ());
795 boundingRect.right = max (boundingRect.right, t1.GetRight ());
798 if (fromMarkerPos <= startOfRow + se.fRealEnd and startOfRow + se.fRealEnd <= toMarkerPos) {
799 Led_Rect t2 = GetCharWindowLocation (FindPreviousCharacter (startOfRow + se.fRealEnd));
800 boundingRect.left = min (boundingRect.left, t2.GetLeft ());
801 boundingRect.right = max (boundingRect.right, t2.GetRight ());
807 if (segmentHilightedAtStart) {
808 boundingRect.left = windowRect.left;
810 if (segmentHilightedAtEnd) {
811 boundingRect.right = windowRect.right;
818 boundingRect.left = windowRect.left;
819 boundingRect.right = windowRect.right;
823 boundingRect.left = max (boundingRect.left, windowRect.left);
824 boundingRect.right = min (boundingRect.right, windowRect.right);
826 Ensure (boundingRect.right >= boundingRect.left);
827 return (boundingRect);
844Led_Rect TextImager::GetIntraRowTextWindowBoundingRect (
size_t fromMarkerPos,
size_t toMarkerPos)
const
846 Require (fromMarkerPos <= toMarkerPos);
848 Led_Rect windowRect = GetWindowRect ();
860 Led_Rect r1 = GetCharWindowLocation (fromMarkerPos);
862 if (fromMarkerPos != toMarkerPos) {
863 r2 = GetCharWindowLocation (FindPreviousCharacter (toMarkerPos));
866 Led_Rect boundingRect = r1;
868 if (GetTextDirection (fromMarkerPos) == eLeftToRight) {
869 boundingRect.left = r1.GetLeft ();
870 boundingRect.right = (fromMarkerPos == toMarkerPos) ? boundingRect.left : r2.GetRight ();
873 boundingRect.right = r1.GetRight ();
874 boundingRect.left = (fromMarkerPos == toMarkerPos) ? boundingRect.right : r2.GetLeft ();
876 Ensure (boundingRect.right >= boundingRect.left);
878 return (boundingRect);
891vector<Led_Rect> TextImager::GetRowHilightRects (
const TextLayoutBlock& text,
size_t rowStart,
size_t rowEnd,
size_t hilightStart,
size_t hilightEnd)
const
893 Require (rowEnd == GetEndOfRowContainingPosition (rowStart));
895 vector<Led_Rect> result;
897 size_t realEndOfRow = GetRealEndOfRowContainingPosition (rowStart);
898 bool segmentHilighted = max (rowStart, hilightStart) < min (realEndOfRow, hilightEnd);
899 if (segmentHilighted) {
900 bool segmentHilightedAtStart =
false;
901 bool segmentHilightedAtEnd =
false;
902 if (GetUseSelectEOLBOLRowHilightStyle ()) {
903 segmentHilightedAtStart = (hilightStart < rowStart) or (hilightStart == 0);
904 segmentHilightedAtEnd = rowEnd < hilightEnd;
905 if (segmentHilightedAtEnd and rowEnd >= GetEnd ()) {
906 segmentHilightedAtEnd =
false;
910 hilightStart = max (hilightStart, rowStart);
912 using ScriptRunElt = TextLayoutBlock::ScriptRunElt;
913 vector<ScriptRunElt> runs = text.GetScriptRuns ();
915 for (
auto i = runs.begin (); i != runs.end (); ++i) {
916 const ScriptRunElt& se = *i;
917 size_t hRunStart = max (se.fRealStart + rowStart, hilightStart);
918 size_t hRunEnd = min (se.fRealEnd + rowStart, hilightEnd);
919 if (hRunStart < hRunEnd) {
920 Led_Rect hilightRect = GetIntraRowTextWindowBoundingRect (hRunStart, hRunEnd);
921 Assert (hilightRect.GetWidth () >= 0);
922 Assert (hilightRect.GetHeight () >= 0);
923 if (not hilightRect.IsEmpty ()) {
924 result.push_back (hilightRect);
930 if (segmentHilightedAtStart) {
931 size_t realOffsetOfVirtualRowStart = rowStart;
932 if (text.GetTextLength () != 0) {
933 realOffsetOfVirtualRowStart += text.MapVirtualOffsetToReal (0);
936 Led_Rect hilightRect = GetCharWindowLocation (realOffsetOfVirtualRowStart);
937 hilightRect.right = hilightRect.left;
938 hilightRect.left = min (GetWindowRect ().GetLeft (), hilightRect.left);
939 Assert (hilightRect.GetWidth () >= 0);
940 Assert (hilightRect.GetHeight () >= 0);
941 if (not hilightRect.IsEmpty ()) {
942 result.push_back (hilightRect);
945 if (segmentHilightedAtEnd) {
946 size_t realOffsetOfVirtualRowEnd = rowStart;
947 if (text.GetTextLength () != 0) {
948 realOffsetOfVirtualRowEnd += text.MapVirtualOffsetToReal (text.GetTextLength () - 1);
951 Led_Rect hilightRect = GetCharWindowLocation (realOffsetOfVirtualRowEnd);
952 hilightRect.left = hilightRect.GetRight ();
953 hilightRect.right = max (hilightRect.right, GetWindowRect ().GetRight ());
954 Assert (hilightRect.GetWidth () >= 0);
955 Assert (hilightRect.GetHeight () >= 0);
956 if (not hilightRect.IsEmpty ()) {
957 result.push_back (hilightRect);
964 for (
auto orit = result.begin (); orit != result.end (); ++orit) {
965 Ensure ((*orit).GetWidth () > 0);
966 Ensure ((*orit).GetHeight () > 0);
967 for (
auto irit = orit + 1; irit != result.end (); ++irit) {
969 Ensure (hr.GetWidth () > 0);
970 Ensure (hr.GetHeight () > 0);
971 Ensure (not Intersect (hr, *orit));
984TextLayoutBlock_Copy TextImager::GetTextLayoutBlock (
size_t rowStart,
size_t rowEnd)
const
986 size_t rowLen = rowEnd - rowStart;
988 CopyOut (rowStart, rowLen, rowBuf.data ());
989 TextLayoutBlock_Basic text{rowBuf.data (), rowBuf.data () + rowLen};
990 return TextLayoutBlock_Copy (text);
1004vector<Led_Rect> TextImager::GetSelectionWindowRects (
size_t from,
size_t to)
const
1006 Require (from <= to);
1008 vector<Led_Rect> result;
1010 from = max (from, GetMarkerPositionOfStartOfWindow ());
1011 to = min (to, FindNextCharacter (GetMarkerPositionOfEndOfWindow ()));
1018 size_t topRow = GetRowContainingPosition (from);
1019 size_t bottomRow = GetRowContainingPosition (to);
1020 Assert (topRow <= bottomRow);
1024 if (GetStartOfRow (bottomRow) == to) {
1026 Assert (topRow < bottomRow);
1030 CoordinateType lastRowBottom = 0;
1031 for (
size_t curRow = topRow;;) {
1032 size_t firstCharInRow = from;
1033 if (topRow != curRow) {
1034 firstCharInRow = GetStartOfRow (curRow);
1037 size_t startOfRow = GetStartOfRowContainingPosition (firstCharInRow);
1038 size_t endOfRow = GetEndOfRowContainingPosition (startOfRow);
1040 TextLayoutBlock_Copy text = GetTextLayoutBlock (startOfRow, endOfRow);
1042 size_t rowLen = endOfRow - startOfRow;
1044 CopyOut (startOfRow, rowLen, rowBuf);
1045 TextLayoutBlock_Basic text{rowBuf, rowBuf + rowLen};
1048 vector<Led_Rect> hilightRects = GetRowHilightRects (text, startOfRow, endOfRow, GetSelectionStart (), GetSelectionEnd ());
1049 CoordinateType newMinTop = lastRowBottom;
1050 CoordinateType newMaxBottom = lastRowBottom;
1051 for (
auto i = hilightRects.begin (); i != hilightRects.end (); ++i) {
1052 Led_Rect hilightRect = *i;
1053 Require (hilightRect.GetWidth () >= 0);
1054 Assert (hilightRect.GetHeight () > 0);
1055 if (not hilightRect.IsEmpty ()) {
1056 result.push_back (hilightRect);
1058 newMinTop = min (newMinTop, hilightRect.top);
1059 newMaxBottom = max (newMaxBottom, hilightRect.bottom);
1067 if (lastRowBottom < newMinTop) {
1070 Led_Rect hScrollAdjustedWR = GetWindowRect () - Led_Point (0, GetHScrollPos ());
1071 result.push_back (Led_Rect (lastRowBottom, hScrollAdjustedWR.GetLeft (), newMinTop - lastRowBottom, hScrollAdjustedWR.GetWidth ()));
1073 lastRowBottom = newMaxBottom;
1075 if (curRow == bottomRow) {
1083 for (
auto orit = result.begin (); orit != result.end (); ++orit) {
1084 Ensure ((*orit).GetWidth () > 0);
1085 Ensure ((*orit).GetHeight () > 0);
1086 for (
auto irit = orit + 1; irit != result.end (); ++irit) {
1087 Led_Rect hr = *irit;
1088 Ensure (hr.GetWidth () > 0);
1089 Ensure (hr.GetHeight () > 0);
1090 Ensure (not Intersect (hr, *orit));
1106void TextImager::GetSelectionWindowRegion (Region* r,
size_t from,
size_t to)
const
1109 vector<Led_Rect> selRects = GetSelectionWindowRects (from, to);
1110 for (
auto i = selRects.begin (); i != selRects.end (); ++i) {
1111 AddRectangleToRegion (*i, r);
1132void TextImager::EraseBackground (Tablet* tablet,
const Led_Rect& subsetToDraw,
bool printing)
1137 tablet->EraseBackground_SolidHelper (subsetToDraw, GetEffectiveDefaultTextColor (TextImager::eDefaultBackgroundColor));
1148void TextImager::HilightArea (Tablet* tablet, Led_Rect hiliteArea)
1151 tablet->HilightArea_SolidHelper (hiliteArea, GetEffectiveDefaultTextColor (eDefaultSelectedTextBackgroundColor),
1152 GetEffectiveDefaultTextColor (eDefaultSelectedTextColor),
1153 GetEffectiveDefaultTextColor (eDefaultBackgroundColor), GetEffectiveDefaultTextColor (eDefaultTextColor));
1163void TextImager::HilightArea (Tablet* tablet,
const Region& hiliteArea)
1166 tablet->HilightArea_SolidHelper (hiliteArea, GetEffectiveDefaultTextColor (eDefaultSelectedTextBackgroundColor),
1167 GetEffectiveDefaultTextColor (eDefaultSelectedTextColor),
1168 GetEffectiveDefaultTextColor (eDefaultBackgroundColor), GetEffectiveDefaultTextColor (eDefaultTextColor));
1182void TextImager::DrawRow (Tablet* tablet,
const Led_Rect& currentRowRect,
const Led_Rect& invalidRowRect,
const TextLayoutBlock& text,
1183 size_t rowStart,
size_t rowEnd,
bool printing)
1186 Require (rowEnd == GetEndOfRowContainingPosition (rowStart));
1196 EraseBackground (tablet, currentRowRect, printing);
1198 DrawRowSegments (tablet, currentRowRect, invalidRowRect, text, rowStart, rowEnd);
1204 DrawRowHilight (tablet, currentRowRect, invalidRowRect, text, rowStart, rowEnd);
1217void TextImager::DrawRowSegments (Tablet* tablet,
const Led_Rect& currentRowRect,
const Led_Rect& invalidRowRect,
1218 const TextLayoutBlock& text,
size_t rowStart,
size_t rowEnd)
1222#if qStroika_Foundation_Debug_AssertionsChecked && 0
1225 size_t startOfRow = GetStartOfRow (row);
1226 size_t endOfRow = GetEndOfRow (row);
1227 size_t realEndOfRow = GetRealEndOfRow (row);
1228 Assert (startOfRow == start);
1229 Assert (endOfRow <= end);
1230 Assert (end <= realEndOfRow);
1244 size_t segEnd = rowEnd;
1245 CoordinateType baseLine = currentRowRect.top + MeasureSegmentBaseLine (rowStart, segEnd);
1251 baseLine = min (baseLine, currentRowRect.bottom);
1253 DrawSegment (tablet, rowStart, segEnd, text, currentRowRect, invalidRowRect, baseLine,
nullptr);
1263void TextImager::DrawRowHilight (Tablet* tablet, [[maybe_unused]]
const Led_Rect& currentRowRect,
const Led_Rect& ,
1264 const TextLayoutBlock& text,
size_t rowStart,
size_t rowEnd)
1266 Require (rowEnd == GetEndOfRowContainingPosition (rowStart));
1268 if (GetSelectionShown ()) {
1269 vector<Led_Rect> hilightRects = GetRowHilightRects (text, rowStart, rowEnd, GetSelectionStart (), GetSelectionEnd ());
1270 for (
auto i = hilightRects.begin (); i != hilightRects.end (); ++i) {
1271 Led_Rect hilightRect = *i;
1274 if (Intersect (hilightRect, currentRowRect) or hilightRect.IsEmpty ()) {
1275 Led_Rect x = hilightRect;
1276 Led_Rect y = currentRowRect;
1279 Assert (Intersect (x, y) or x.IsEmpty ());
1282 HilightArea (tablet, hilightRect);
1294void TextImager::DrawInterLineSpace (DistanceType interlineSpace, Tablet* tablet, CoordinateType vPosOfTopOfInterlineSpace,
1295 bool segmentHilighted,
bool printing)
1301 if (interlineSpace != 0) {
1302 Led_Rect fillRect = GetWindowRect ();
1303 fillRect.top = vPosOfTopOfInterlineSpace;
1304 fillRect.bottom = vPosOfTopOfInterlineSpace + interlineSpace;
1305 EraseBackground (tablet, fillRect, printing);
1306 if (segmentHilighted) {
1307 HilightArea (tablet, fillRect);
1328bool TextImager::ContainsMappedDisplayCharacters (
const Led_tChar* ,
size_t )
const
1342void TextImager::ReplaceMappedDisplayCharacters (
const Led_tChar* srcText, Led_tChar* copyText,
size_t nTChars)
const
1345 (void)::memcpy (copyText, srcText, nTChars *
sizeof (Led_tChar));
1358size_t TextImager::RemoveMappedDisplayCharacters (Led_tChar* ,
size_t nTChars)
const
1375void TextImager::PatchWidthRemoveMappedDisplayCharacters (
const Led_tChar* , DistanceType* ,
size_t )
const
1384bool TextImager::ContainsMappedDisplayCharacters_HelperForChar (
const Led_tChar* text,
size_t nTChars, Led_tChar charToMap)
1387 const Led_tChar* end = &text[nTChars];
1388 for (
const Led_tChar* cur = text; cur < end; cur = Led_NextChar (cur)) {
1389 if (*cur == charToMap) {
1402void TextImager::ReplaceMappedDisplayCharacters_HelperForChar (Led_tChar* copyText,
size_t nTChars, Led_tChar charToMap, Led_tChar charToMapTo)
1405 Led_tChar* end = ©Text[nTChars];
1406 for (Led_tChar* cur = copyText; cur < end; cur = Led_NextChar (cur)) {
1407 if (*cur == charToMap) {
1419size_t TextImager::RemoveMappedDisplayCharacters_HelperForChar (Led_tChar* copyText,
size_t nTChars, Led_tChar charToRemove)
1422 Led_tChar* outPtr = copyText;
1423 Led_tChar* end = copyText + nTChars;
1424 for (
const Led_tChar* cur = copyText; cur < end; cur = Led_NextChar (cur)) {
1425 if (*cur != charToRemove) {
1427 outPtr = Led_NextChar (outPtr);
1430 size_t newLen = outPtr - copyText;
1431 Assert (newLen <= nTChars);
1441void TextImager::PatchWidthRemoveMappedDisplayCharacters_HelperForChar (
const Led_tChar* srcText, DistanceType* distanceResults,
1442 size_t nTChars, Led_tChar charToRemove)
1446 DistanceType cumSubtract = 0;
1447 const Led_tChar* end = srcText + nTChars;
1448 for (
const Led_tChar* cur = srcText; cur < end; cur = Led_NextChar (cur)) {
1449 size_t i = cur - srcText;
1450 Assert (i < nTChars);
1451 if (*cur == charToRemove) {
1452 DistanceType thisSoftBreakWidth = i == 0 ? distanceResults[0] : (distanceResults[i] - distanceResults[i - 1]);
1453 cumSubtract = thisSoftBreakWidth;
1455 distanceResults[i] -= cumSubtract;
1472void TextImager::DrawSegment (Tablet* tablet,
size_t from,
size_t to,
const TextLayoutBlock& text,
const Led_Rect& drawInto,
1473 const Led_Rect& , CoordinateType useBaseLine, DistanceType* pixelsDrawn)
1475 DrawSegment_ (tablet, GetDefaultFont (), from, to, text, drawInto, useBaseLine, pixelsDrawn);
1487void TextImager::DrawSegment_ (Tablet* tablet,
const FontSpecification& fontSpec,
size_t from,
size_t to,
const TextLayoutBlock& text,
1488 const Led_Rect& drawInto, CoordinateType useBaseLine, DistanceType* pixelsDrawn)
const
1491 Assert (from <= to);
1501 [[maybe_unused]]
size_t length = to - from;
1506 Color foreColor = fontSpec.GetTextColor ();
1507 Color backColor = GetEffectiveDefaultTextColor (eDefaultBackgroundColor);
1508 tablet->SetBackColor (backColor);
1509 tablet->SetForeColor (foreColor);
1511 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1513 DistanceType ascent = fCachedFontInfo.GetAscent ();
1514 Assert (useBaseLine >= drawInto.top);
1517 CoordinateType drawCharTop = useBaseLine - ascent;
1520 if (fontSpec.GetStyle_SubOrSuperScript () == FontSpecification::eSuperscript) {
1526 drawCharTop = useBaseLine - ascent * 3 / 2;
1528 else if (fontSpec.GetStyle_SubOrSuperScript () == FontSpecification::eSubscript) {
1529 drawCharTop += fCachedFontInfo.GetDescent ();
1532 using ScriptRunElt = TextLayoutBlock::ScriptRunElt;
1533 vector<ScriptRunElt> runs = text.GetScriptRuns ();
1534 Assert (not runs.empty () or (length == 0));
1535 if (runs.size () > 1) {
1537 sort (runs.begin (), runs.end (), TextLayoutBlock::LessThanVirtualStart ());
1540 const Led_tChar* fullVirtualText = text.PeekAtVirtualText ();
1541 Led_Point outputAt = Led_Point (drawCharTop, drawInto.left);
1542 if (pixelsDrawn !=
nullptr) {
1545 for (
auto i = runs.begin (); i != runs.end (); ++i) {
1546 const ScriptRunElt& se = *i;
1547 size_t runLength = se.fVirtualEnd - se.fVirtualStart;
1553 (void)::memcpy (
static_cast<Led_tChar*
> (useVirtualText), &fullVirtualText[se.fVirtualStart], runLength *
sizeof (Led_tChar));
1558 Led_tChar* drawText = useVirtualText.
data ();
1559 size_t drawTextLen = runLength;
1561 if (ContainsMappedDisplayCharacters (drawText, drawTextLen)) {
1563 ReplaceMappedDisplayCharacters (drawText, mappedDisplayBuf.data (), drawTextLen);
1564 size_t newLength = RemoveMappedDisplayCharacters (mappedDisplayBuf.data (), drawTextLen);
1565 Assert (newLength <= drawTextLen);
1566 drawText = mappedDisplayBuf.data ();
1567 drawTextLen = newLength;
1573 DistanceType amountDrawn = 0;
1574 tablet->TabbedTextOut (fCachedFontInfo, drawText, drawTextLen, se.fDirection, outputAt, GetWindowRect ().left,
1575 GetTabStopList (from), &amountDrawn, GetHScrollPos ());
1576 outputAt.h += amountDrawn;
1577 if (pixelsDrawn !=
nullptr) {
1578 *pixelsDrawn += amountDrawn;
1583void TextImager::MeasureSegmentWidth (
size_t from,
size_t to,
const Led_tChar* text, DistanceType* distanceResults)
const
1585 MeasureSegmentWidth_ (GetDefaultFont (), from, to, text, distanceResults);
1595void TextImager::MeasureSegmentWidth_ (
const FontSpecification& fontSpec,
size_t from,
size_t to,
const Led_tChar* text, DistanceType* distanceResults)
const
1597 Require (to > from);
1599 Tablet_Acquirer tablet (
this);
1601 size_t length = to - from;
1602 Assert (length > 0);
1604 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1606 if (ContainsMappedDisplayCharacters (text, length)) {
1608 ReplaceMappedDisplayCharacters (text, buf2.data (), length);
1609 tablet->MeasureText (fCachedFontInfo, buf2.data (), length, distanceResults);
1610 PatchWidthRemoveMappedDisplayCharacters (buf2.data (), distanceResults, length);
1613 tablet->MeasureText (fCachedFontInfo, text, length, distanceResults);
1617DistanceType TextImager::MeasureSegmentHeight (
size_t from,
size_t to)
const
1619 return (MeasureSegmentHeight_ (GetDefaultFont (), from, to));
1622DistanceType TextImager::MeasureSegmentHeight_ (
const FontSpecification& fontSpec,
size_t ,
size_t )
const
1624 Tablet_Acquirer tablet (
this);
1626 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1627 return (fCachedFontInfo.GetLineHeight ());
1630DistanceType TextImager::MeasureSegmentBaseLine (
size_t from,
size_t to)
const
1632 return (MeasureSegmentBaseLine_ (GetDefaultFont (), from, to));
1635DistanceType TextImager::MeasureSegmentBaseLine_ (
const FontSpecification& fontSpec,
size_t ,
size_t )
const
1637 Tablet_Acquirer tablet (
this);
1639 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1640 return fCachedFontInfo.GetAscent ();
1643FontMetrics TextImager::GetFontMetricsAt ([[maybe_unused]]
size_t charAfterPos)
const
1645 Tablet_Acquirer tablet (
this);
1650 FontCacheInfoUpdater fontCacheUpdater{
this, tablet, fontSpec};
1651 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< qTargetPlatformSDKUseswchar_t, wchar_t, char > SDKChar