Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
PartitioningTextImager.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Led_PartitioningTextImager_h_
5#define _Stroika_Frameworks_Led_PartitioningTextImager_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Cache/LRUCache.h"
10
11/*
12@MODULE: PartitioningTextImager
13@DESCRIPTION:
14 <p>@'PartitioningTextImager' is based on the idea that a entire text buffer can be layed out
15 by first breaking the text into special chunks, and then laying out those chunks.</p>
16 <p>We use @'Marker's (or more specificly @'Partition::PartitionMarker') to delimit
17 the chunks (refered to as partition elements).</p>
18 <p>Note that it doesn't really matter much what the rule is for breaking text
19 into chunks, except that the rule should be chosen to make the drawing of those chunks easier,
20 and to fit in well with logical clipping.</p>
21 <p>So for example, the most common rule for breaking text into partitions would be to look
22 for LF chraracters, and consider each "line" to be a separate partition element.</p>
23*/
24
25#include "TextImager.h"
26
27namespace Stroika::Frameworks::Led {
28
29 /*
30 @CONFIGVAR: qCacheTextMeasurementsForPM
31 @DESCRIPTION: <p>A fairly simple, but effective performance hack. Defaults ON</p>
32 */
33#ifndef qCacheTextMeasurementsForPM
34#define qCacheTextMeasurementsForPM 1
35#endif
36
37 /*
38 @CLASS: Partition
39 @BASES: virtual @'MarkerOwner'
40 @DESCRIPTION: <p>PartitioningTextImager is a TextImager which implements imaging by partitioning the text into stable
41 regions, called Partitions.</p>
42 <p>A partition is logically very similar to a @'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>'. But we
43 don't directly use that class because it is not an abstract class, but rather a utility template. And that utility
44 has very different performance considerations than we require. Also, that class is used to associate a MARKERINFO
45 with a particular partition element. There is nothing we here associate with the partition element.</p>
46 <p>For more information, see <a href=../DesignManual/TextImagers.html>DesignManual/TextImagers.html</a>
47 (NB: This code has changed ALOT between Led 2.2 and 2.3, and so this doc really needs to be rewritten!).</p>
48 */
49 class Partition : public virtual MarkerOwner {
50 private:
51 using inherited = MarkerOwner;
52
53 public:
54 Partition (TextStore& textStore);
55 virtual ~Partition ();
56
57 protected:
58 virtual void FinalConstruct ();
59#if qStroika_Foundation_Debug_AssertionsChecked
60 private:
61 bool fFinalConstructCalled;
62#endif
63
64 public:
65 class PartitionMarker;
66
67 public:
68 virtual TextStore* PeekAtTextStore () const override;
69
70 private:
71 TextStore& fTextStore;
72
73 protected:
74 virtual void UpdatePartitions (PartitionMarker* pm, const UpdateInfo& updateInfo) noexcept = 0;
75
76 public:
77 virtual PartitionMarker* GetPartitionMarkerContainingPosition (size_t charPosition) const;
78
79 private:
80 mutable PartitionMarker* fFindContainingPMCache;
81
82 protected:
83 virtual PartitionMarker* MakeNewPartitionMarker (PartitionMarker* insertAfterMe); // if insertAfter==nullptr then prepend
84
85 public:
86 class PartitionWatcher;
87 nonvirtual void AddPartitionWatcher (PartitionWatcher* watcher);
88 nonvirtual void RemovePartitionWatcher (PartitionWatcher* watcher);
89
90 private:
91 vector<PartitionWatcher*> fPartitionWatchers;
92
93 protected:
94 virtual void Split (PartitionMarker* pm, size_t at);
95 virtual void Coalece (PartitionMarker* pm); // call when pm loses its trialing NL (may do nothing)
96 private:
97 nonvirtual void DoAboutToSplitCalls (PartitionMarker* pm, size_t at, vector<void*>* infos) const noexcept;
98 nonvirtual void DoDidSplitCalls (const vector<void*>& infos) const noexcept;
99 nonvirtual void DoAboutToCoaleceCalls (PartitionMarker* pm, vector<void*>* infos) const noexcept;
100 nonvirtual void DoDidCoaleceCalls (const vector<void*>& infos) const noexcept;
101
102 protected:
103 nonvirtual void AccumulateMarkerForDeletion (PartitionMarker* m);
104 MarkerMortuary<PartitionMarker> fMarkersToBeDeleted;
105
106 protected:
107 virtual void AboutToUpdateText (const UpdateInfo& updateInfo) override;
108 virtual void DidUpdateText (const UpdateInfo& updateInfo) noexcept override;
109
110 // Trivial TextStore wrappers...
111 public:
112 nonvirtual size_t GetEnd () const;
113 nonvirtual void CopyOut (size_t from, size_t count, Led_tChar* buffer) const;
114
115 public:
116 nonvirtual PartitionMarker* GetFirstPartitionMarker () const;
117 nonvirtual PartitionMarker* GetLastPartitionMarker () const;
118
119 private:
120#if qStroika_Foundation_Debug_AssertionsChecked
121 size_t fPartitionMarkerCount;
122#endif
123 PartitionMarker* fPartitionMarkerFirst;
124 PartitionMarker* fPartitionMarkerLast;
125
126 // Debug support
127 public:
128 nonvirtual void Invariant () const;
129#if qStroika_Foundation_Debug_AssertionsChecked
130 protected:
131 virtual void Invariant_ () const;
132#endif
133 private:
134 friend class PartitionMarker; // For UpdatePartitions () call
135 };
136
137 /*
138 @CLASS: Partition::PartitionMarker
139 @BASES: @'Marker'
140 @DESCRIPTION: <p>This class is the building block of a partition. It is used to keep track of a single partition element.</p>
141 <p><em>Subclassing Caution</em>: Since we provide operator new/delete overrides, you must be careful
142 building any subclasses which have a different size than this class. You must provide your own overrides
143 of operator new/delete todo so.</p>
144 */
145 class Partition::PartitionMarker : public Marker, public Foundation::Memory::UseBlockAllocationIfAppropriate<PartitionMarker> {
146 private:
147 using inherited = Marker;
148
149 public:
150 PartitionMarker (Partition& owner, PartitionMarker* insertAfterMe); // if insertAfter==nullptr then prepend
151
152 // Calls Partition::UpdatePartitions ()
153 public:
154 virtual void DidUpdateText (const UpdateInfo& updateInfo) noexcept override;
155
156 // These markers are all kept in a doubly-linked list, headed by "GetOwner ()"
157 public:
158 virtual PartitionMarker* GetPrevious () const;
159 virtual PartitionMarker* GetNext () const;
160
161 private:
162 PartitionMarker* fPrevious;
163 PartitionMarker* fNext;
164
165 public:
166 nonvirtual Partition& GetOwner () const;
167
168 private:
169 friend class Partition;
170 };
171
172 /*
173 @CLASS: Partition::PartitionWatcher
174 @DESCRIPTION: <p>Some programs may need to keep track of when a partition changes. For example, an Imager will likely want to redisplay
175 itself. Perhaps even do some more elaborate computation. Things like paragraph-based marker-covers (@'MarkerCover<MARKER,MARKERINFO,INCREMENTALMARKERINFO>')
176 may need to adjust their boundaries to correspond to the new paragraph boundaries
177 (e.g. @'WordProcessor::ParagraphDatabaseRep').</p>
178 <p>You get notified just before, and after partition markers are split, and/or coalesced. The untyped 'info' records are used to
179 pass arbitrary bits of info from the 'about' call to the 'did' call.</p>
180 */
181 class Partition::PartitionWatcher {
182 public:
183 virtual void AboutToSplit (PartitionMarker* pm, size_t at, void** infoRecord) const noexcept = 0;
184 virtual void DidSplit (void* infoRecord) const noexcept = 0;
185 virtual void AboutToCoalece (PartitionMarker* pm, void** infoRecord) const noexcept = 0;
186 virtual void DidCoalece (void* infoRecord) const noexcept = 0;
187 };
188
189#if qStroika_Frameworks_Led_SupportGDI
190 /*
191 @CLASS: PartitioningTextImager
192 @BASES: virtual @'TextImager'
193 @DESCRIPTION: <p>PartitioningTextImager is a TextImager which implements imaging by partitioning the text into stable
194 regions, called Partitions.</p>
195 <p>For more information, see <a href=../DesignManual/TextImagers.html>DesignManual/TextImagers.html</a>.</p>
196 */
197 class PartitioningTextImager : public virtual TextImager {
198 private:
199 using inherited = TextImager;
200
201 protected:
202 PartitioningTextImager ();
203 virtual ~PartitioningTextImager ();
204
205 public:
206 using PartitionPtr = shared_ptr<Partition>;
207 nonvirtual PartitionPtr GetPartition () const;
208
209 protected:
210 virtual void SetPartition (const PartitionPtr& partitionPtr);
211
212 private:
213 PartitionPtr fPartition;
214
215 public:
216 virtual PartitionPtr MakeDefaultPartition () const = 0;
217
218#if qCacheTextMeasurementsForPM
219 private:
220 class MeasureTextCache;
221 unique_ptr<MeasureTextCache> fMeasureTextCache;
222
223 protected:
224 virtual void InvalidateAllCaches () override;
225#endif
226
227 public:
228 using PartitionMarker = Partition::PartitionMarker;
229
230 // Simple wrappers on the Partition object
231 protected:
232 nonvirtual PartitionMarker* GetFirstPartitionMarker () const;
233
234 public:
235 nonvirtual size_t GetStartOfPartitionContainingPosition (size_t charPosition) const;
236 nonvirtual size_t GetEndOfPartitionContainingPosition (size_t charPosition) const;
237 nonvirtual PartitionMarker* GetPartitionMarkerContainingPosition (size_t charPosition) const;
238
239 public:
240 virtual TextDirection GetPrimaryPartitionTextDirection (size_t rowContainingCharPosition) const;
241
242 public:
243 virtual TextLayoutBlock_Copy GetTextLayoutBlock (size_t rowStart, size_t rowEnd) const override;
244
245 public:
246 virtual TextDirection GetTextDirection (size_t charPosition) const override;
247
248 public:
249 virtual DistanceType CalcSegmentSize (size_t from, size_t to) const override;
250
251 public:
252 virtual void GetRowRelativeCharLoc (size_t charLoc, DistanceType* lhs, DistanceType* rhs) const override;
253 virtual size_t GetRowRelativeCharAtLoc (CoordinateType hOffset, size_t rowStart) const override;
254
255 private:
256 nonvirtual DistanceType CalcSegmentSize_REFERENCE (size_t from, size_t to) const;
257#if qCacheTextMeasurementsForPM
258 nonvirtual DistanceType CalcSegmentSize_CACHING (size_t from, size_t to) const;
259#endif
260
261 private:
262 nonvirtual void CalcSegmentSize_FillIn (size_t rowStart, size_t rowEnd, DistanceType* distanceVector) const;
263
264 protected:
265 virtual size_t ResetTabStops (size_t from, const Led_tChar* text, size_t nTChars, DistanceType* charLocations, size_t startSoFar) const;
266
267 // Debug support
268 public:
269 nonvirtual void Invariant () const;
270#if qStroika_Foundation_Debug_AssertionsChecked
271 protected:
272 virtual void Invariant_ () const;
273#endif
274 };
275
276#if qCacheTextMeasurementsForPM
277 /*
278 @CLASS: PartitioningTextImager::MeasureTextCache
279 @BASES: private @'Partition::PartitionWatcher', @'MarkerOwner'
280 @DESCRIPTION: <p>A helper class to implement the @'qCacheTextMeasurementsForPM' caching code.</p>
281 */
282 class PartitioningTextImager::MeasureTextCache : private Partition::PartitionWatcher, public MarkerOwner {
283 private:
284 using inherited = void*; // so any references to inherited generate an error...
285
286 public:
287 MeasureTextCache (const PartitionPtr& partition);
288 ~MeasureTextCache ();
289
290 public:
291 virtual void AboutToSplit (PartitionMarker* pm, size_t at, void** infoRecord) const noexcept override;
292 virtual void DidSplit (void* infoRecord) const noexcept override;
293 virtual void AboutToCoalece (PartitionMarker* pm, void** infoRecord) const noexcept override;
294 virtual void DidCoalece (void* infoRecord) const noexcept override;
295
296 public:
297 virtual TextStore* PeekAtTextStore () const override;
298
299 protected:
300 virtual void EarlyDidUpdateText (const UpdateInfo& updateInfo) noexcept override;
301
302 public:
303 struct CacheEltLRUCacheTraits;
304 class CacheElt {
305 public:
306 struct COMPARE_ITEM {
307 COMPARE_ITEM (PartitionMarker* pm, size_t startingRowAt)
308 : fPM (pm)
309 , fRowStartingAt (startingRowAt)
310 {
311 }
312 PartitionMarker* fPM;
313 size_t fRowStartingAt;
314 };
315
316 public:
317 CacheElt ();
318 CacheElt (const COMPARE_ITEM& ci);
319 CacheElt (const CacheElt&) = default;
320
321 private:
322 COMPARE_ITEM fValidFor;
323
324 public:
325 Foundation::Memory::InlineBuffer<DistanceType> fMeasurementsCache; // for just the given PM
326
327 private:
328 friend struct CacheEltLRUCacheTraits;
329 friend class PartitioningTextImager::MeasureTextCache;
330 };
331
332 private:
333 struct CacheElt_COMPARE_ITEM_KeyEqualsCompareFunctionType_ {
334 bool operator() (const CacheElt::COMPARE_ITEM& lhs, const CacheElt::COMPARE_ITEM& rhs) const
335 {
336 return lhs.fPM == rhs.fPM and lhs.fRowStartingAt == rhs.fRowStartingAt;
337 };
338 };
339 mutable Foundation::Cache::LRUCache<CacheElt::COMPARE_ITEM, CacheElt, CacheElt_COMPARE_ITEM_KeyEqualsCompareFunctionType_> fCache;
340
341 public:
342 nonvirtual void ClearAll ();
343
344 public:
345 nonvirtual CacheElt LookupValue (PartitionMarker* pm, size_t rowStart, const function<CacheElt (PartitionMarker*, size_t)>& valueFetcher);
346
347 private:
348 PartitionPtr fPartition;
349 };
350#endif
351#endif
352
353}
354
355/*
356 ********************************************************************************
357 ***************************** Implementation Details ***************************
358 ********************************************************************************
359 */
360#include "PartitioningTextImager.inl"
361
362#endif /*_Stroika_Frameworks_Led_PartitioningTextImager_h_*/