Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
StyledTextImager.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Frameworks_Led_StyledTextImager_h_
5#define _Stroika_Frameworks_Led_StyledTextImager_h_ 1
6
7#include "Stroika/Frameworks/StroikaPreComp.h"
8
9#include "Stroika/Frameworks/Led/MarkerCover.h"
10#include "Stroika/Frameworks/Led/Support.h"
11#include "Stroika/Frameworks/Led/TextImager.h"
12
13/*
14@MODULE: StyledTextImager
15@DESCRIPTION:
16 <p>Basic foundations of display of 'styled', or marked up, text. The central
17 class of this module is @'StyledTextImager'</p>
18
19 */
20
21#if qStroika_Foundation_Common_Platform_MacOS
22struct TextStyle;
23struct ScrpSTElement;
24#endif
25
26namespace Stroika::Frameworks::Led {
27
28/**
29 * Debugging hack for @'StyledTextImager::StyleMarkerSummarySink::CombineElements'.
30 * It CAN cause problems having multiple markers of the same priority overlapping, so this
31 * code will assert-out / warn when that happens. It COULD be OK - so thats why this is optional
32 * checking.
33 */
34#ifndef qStroika_Frameworks_Led_AssertWarningForEqualPriorityMarkers
35#define qStroika_Frameworks_Led_AssertWarningForEqualPriorityMarkers qStroika_Foundation_Debug_AssertionsChecked
36#endif
37
38 class StyleMarker;
39
40 /*
41 @CLASS: StyleRunElement
42 @DESCRIPTION: <p>A simple summary structure typically used from
43 @'StandardStyledTextImager::SummarizeStyleMarkers', or subclasses, to represent the net effect of overlapping
44 style runs.</p>
45 <p><code><pre>
46 struct StyleRunElement {
47 ...
48 StyleMarker* fMarker;
49 size_t fLength;
50 vector&lt;StyleMarker*&gt; fSupercededMarkers;
51 ...
52 };
53 </pre></code></p>
54 */
55 struct StyleRunElement {
56 StyleRunElement (StyleMarker* marker, size_t length);
57
58 StyleMarker* fMarker{nullptr};
59 size_t fLength{0};
60 vector<StyleMarker*> fSupercededMarkers;
61 };
62
63#if qStroika_Frameworks_Led_SupportGDI
64 class StyledTextImager;
65#endif
66
67 /*
68 @CLASS: StyledTextImager::StyleMarker
69 @BASES: @'Marker'
70 @DESCRIPTION: <p>Abstract class - subclass this to provide custom style drawing. Managed by
71 class @'StyledTextImager'.</p>
72 */
73 class StyleMarker : public Marker {
74 protected:
75 StyleMarker () = default;
76
77 public:
78 enum {
79 eBaselinePriority = 0
80 };
81
82 public:
83 virtual int GetPriority () const;
84
85#if qStroika_Frameworks_Led_SupportGDI
86 public:
87 /*
88 @METHOD: StyledTextImager::StyleMarker::DrawSegment
89 @DESCRIPTION: <p>This pure-virtual hook function is called when the given range of text needs to be
90 drawn. (SHOULD EXPLAIN THIS ALOT MORE!!!)</p>
91 <p>NB: an extra 'invalidRect' argument was added to this method in Led 3.1a6. Note that
92 this is incompatible change.</p>
93 */
94 virtual void DrawSegment (const StyledTextImager* imager, const StyleRunElement& runElement, Tablet* tablet, size_t from, size_t to,
95 const TextLayoutBlock& text, const Led_Rect& drawInto, const Led_Rect& invalidRect,
96 CoordinateType useBaseLine, DistanceType* pixelsDrawn) = 0;
97
98 /*
99 @METHOD: StyledTextImager::StyleMarker::MeasureSegmentWidth
100 @DESCRIPTION: <p>This pure-virtual hook function is called when the given range of text needs to be
101 measured (character widths). (SHOULD EXPLAIN THIS ALOT MORE!!!)</p>
102 */
103 virtual void MeasureSegmentWidth (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to,
104 const Led_tChar* text, DistanceType* distanceResults) const = 0;
105 /*
106 @METHOD: StyledTextImager::StyleMarker::MeasureSegmentWidth
107 @DESCRIPTION: <p>This pure-virtual hook function is called when the given range of text needs to be
108 measured (hieght of text segment). (SHOULD EXPLAIN THIS ALOT MORE!!!)</p>
109 */
110 virtual DistanceType MeasureSegmentHeight (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to) const = 0;
111
112 /*
113 @METHOD: StyledTextImager::StyleMarker::MeasureSegmentBaseLine
114 @DESCRIPTION: <p>This pure-virtual hook function is called when the given range of text needs to be
115 measured (baseline of text segment). (SHOULD EXPLAIN THIS ALOT MORE!!!)</p>
116 */
117 virtual DistanceType MeasureSegmentBaseLine (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to) const = 0;
118#endif
119 };
120
121 /*
122 @CLASS: StyledTextImager::StyleMarkerSummarySink
123 @BASES: @'TextStore::MarkerSink'
124 @DESCRIPTION: <p>A marker sink used in calls to @'TextStore::CollectAllMarkersInRangeInto' to extract from the textstore
125 the style marker information which applies to a given region of text. This is typically used from
126 @'StandardStyledTextImager::SummarizeStyleMarkers', or subclasses. And you would override it (and that method)
127 to provide an alternate mechanism for combining/interpretting style markers within a region (say when the overlap).</p>
128 */
129 class StyleMarkerSummarySink : public TextStore::MarkerSink {
130 private:
131 using inherited = TextStore::MarkerSink;
132
133 public:
134 StyleMarkerSummarySink (size_t from, size_t to);
135 StyleMarkerSummarySink (size_t from, size_t to, const TextLayoutBlock& text);
136
137 public:
138 nonvirtual vector<StyleRunElement> ProduceOutputSummary () const;
139
140 public:
141 virtual void Append (Marker* m) override;
142
143 protected:
144 virtual void CombineElements (StyleRunElement* runElement, StyleMarker* newStyleMarker);
145
146 private:
147 nonvirtual void SplitIfNeededAt (size_t markerPos);
148
149 private:
150 vector<StyleRunElement> fBuckets;
151 const TextLayoutBlock* fText; // make this a reference once I've gotten rid of CTOR that takes no TextLayoutBlock - LGP 2002-12-16
152 size_t fFrom;
153 size_t fTo;
154 };
155
156 /*
157 @CLASS: StyledTextImager::StyleMarkerSummarySinkForSingleOwner
158 @BASES: @'StyledTextImager::StyleMarkerSummarySink'
159 @DESCRIPTION: <p>Ignore style markers from an owner other than the one given as argument in the constructor.</p>
160 */
161 class StyleMarkerSummarySinkForSingleOwner : public StyleMarkerSummarySink {
162 private:
163 using inherited = StyleMarkerSummarySink;
164
165 public:
166 StyleMarkerSummarySinkForSingleOwner (const MarkerOwner& owner, size_t from, size_t to);
167 StyleMarkerSummarySinkForSingleOwner (const MarkerOwner& owner, size_t from, size_t to, const TextLayoutBlock& text);
168
169 protected:
170 virtual void CombineElements (StyleRunElement* runElement, StyleMarker* newStyleMarker) override;
171
172 private:
173 const MarkerOwner& fOwner;
174 };
175
176 /*
177 @CLASS: SimpleStyleMarkerByFontSpec<BASECLASS>
178 @BASES: BASECLASS = @'StyledTextImager::StyleMarker'
179 @DESCRIPTION: <p>Very frequently, you will want to implement a style-marker which just uses some @'FontSpecification'</p>
180 and applies that to the given text, in a manner vaguely similar to what @'StandardStyledTextImager::StandardStyleMarker' does.
181 <p>This class is an abstract class, where you must specify the particular @'FontSpecification' by overriding
182 the pure-virtual @'SimpleStyleMarkerByFontSpec::MakeFontSpec'. That font-spec is then used in the various
183 DrawSegemnt () etc overrides.</p>
184 <p>This class is not intended to be an abstract interface one programs to, but rather a helper class for subclasses
185 of the abstract @'StyledTextImager::StyleMarker' class. So you are not encouraged to declare variables of the type
186 <code>SimpleStyleMarkerByFontSpec<>*</code>. Just use the class as a helper.</p>
187 <p>See @'TrivialFontSpecStyleMarker' for an even simpler class to use.</p>
188 */
189 template <class BASECLASS = StyleMarker>
190 class SimpleStyleMarkerByFontSpec : public BASECLASS {
191 private:
192 using inherited = BASECLASS;
193
194 protected:
195 SimpleStyleMarkerByFontSpec () = default;
196
197#if qStroika_Frameworks_Led_SupportGDI
198 protected:
199 /*
200 @METHOD: SimpleStyleMarkerByFontSpec<BASECLASS>::MakeFontSpec
201 @DESCRIPTION: <p>Virtual method which subclasses override to specify how <em>they</em> want to have the given text displayed.
202 Hopefully enough context is passed into this function to make this helper class widely applicable. All it must do is return
203 a simple @'FontSpecification' result, and that will be used for all measurements and
204 display of this marker.</p>
205 <p>By default, it just returns the default font associated with the imager.</p>
206 */
207 virtual FontSpecification MakeFontSpec (const StyledTextImager* imager, const StyleRunElement& runElement) const;
208#endif
209
210#if qStroika_Frameworks_Led_SupportGDI
211 public:
212 virtual void DrawSegment (const StyledTextImager* imager, const StyleRunElement& runElement, Tablet* tablet, size_t from, size_t to,
213 const TextLayoutBlock& text, const Led_Rect& drawInto, const Led_Rect& /*invalidRect*/,
214 CoordinateType useBaseLine, DistanceType* pixelsDrawn) override;
215 virtual void MeasureSegmentWidth (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to,
216 const Led_tChar* text, DistanceType* distanceResults) const override;
217 virtual DistanceType MeasureSegmentHeight (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to) const override;
218 virtual DistanceType MeasureSegmentBaseLine (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to) const override;
219#endif
220 };
221
222 /*
223 @CLASS: SimpleStyleMarkerByIncrementalFontSpec<BASECLASS>
224 @BASES: BASECLASS (which should be a subclass of @'SimpleStyleMarkerByFontSpec<BASECLASS>')
225 @DESCRIPTION: <p>This helper can be used to avoid manually subclassing @'SimpleStyleMarkerByFontSpec<BASECLASS>'
226 and instead, just takes an @'IncrementalFontSpecification' and does the right thing - mapping
227 that into what is displayed.</p>
228 */
229 template <class BASECLASS>
230 class SimpleStyleMarkerByIncrementalFontSpec : public BASECLASS {
231 private:
232 using inherited = BASECLASS;
233
234 public:
235 SimpleStyleMarkerByIncrementalFontSpec (const IncrementalFontSpecification& styleInfo = IncrementalFontSpecification ());
236
237#if qStroika_Frameworks_Led_SupportGDI
238 protected:
239 virtual FontSpecification MakeFontSpec (const StyledTextImager* imager, const StyleRunElement& runElement) const override;
240#endif
241
242 public:
243 IncrementalFontSpecification fFontSpecification;
244 };
245
246 /*
247 @CLASS: TrivialFontSpecStyleMarker
248 @BASES: @'SimpleStyleMarkerByIncrementalFontSpec<BASECLASS>' with BASECLASS = @'SimpleStyleMarkerByFontSpec<BASECLASS>'.
249 @DESCRIPTION: <p>This class just adds to @'SimpleStyleMarkerByFontSpec' a field which is the @'IncrementalFontSpecification'.
250 This is <em>not</em> intended to be subclassed. If you do subclass - beware the overload of operator new () and
251 block-allocation usage. Or better yet, subclass @'SimpleStyleMarkerByIncrementalFontSpec<BASECLASS>' instead.</p>
252 */
253 class TrivialFontSpecStyleMarker : public SimpleStyleMarkerByIncrementalFontSpec<SimpleStyleMarkerByFontSpec<>>,
254 public Foundation::Memory::UseBlockAllocationIfAppropriate<TrivialFontSpecStyleMarker> {
255 private:
256 using inherited = SimpleStyleMarkerByIncrementalFontSpec<SimpleStyleMarkerByFontSpec<>>;
257
258 public:
259 TrivialFontSpecStyleMarker (const IncrementalFontSpecification& styleInfo);
260
261 public:
262 virtual int GetPriority () const override;
263 };
264
265 /*
266 @CLASS: SimpleStyleMarkerWithExtraDraw<BASECLASS>
267 @BASES: @'SimpleStyleMarkerByFontSpec<BASECLASS>' with BASECLASS defaulting to @'StyledTextImager::StyleMarker'
268 @DESCRIPTION: <p>Very frequently, you will want to implement a style-marker which just draws some
269 extra stuff drawn at the end.</p>
270 <p>This class is an abstract class, where you must specify the particular extra drawing in the
271 @'SimpleStyleMarkerWithExtraDraw<BASECLASS>::DrawExtra' method.</p>
272 <p>See also @'SimpleStyleMarkerWithLightUnderline<BASECLASS>'.</p>
273 */
274 template <typename BASECLASS = StyleMarker>
275 class SimpleStyleMarkerWithExtraDraw : public SimpleStyleMarkerByFontSpec<BASECLASS> {
276 private:
277 using inherited = SimpleStyleMarkerByFontSpec<BASECLASS>;
278
279 protected:
280 SimpleStyleMarkerWithExtraDraw () = default;
281
282#if qStroika_Frameworks_Led_SupportGDI
283 protected:
284 /*
285 @METHOD: SimpleStyleMarkerWithExtraDraw<BASECLASS>::DrawExtra
286 @DESCRIPTION: <p>Pure virtual method which subclasses override to specify how <em>they</em> want to
287 draw (some additional markup - e.g. an underline).</p>
288 */
289 virtual void DrawExtra (const StyledTextImager* imager, const StyleRunElement& runElement, Tablet* tablet, size_t from, size_t to,
290 const TextLayoutBlock& text, const Led_Rect& drawInto, CoordinateType useBaseLine, DistanceType pixelsDrawn) = 0;
291#endif
292
293 protected:
294 virtual StyleRunElement MungeRunElement (const StyleRunElement& inRunElt) const;
295
296#if qStroika_Frameworks_Led_SupportGDI
297 public:
298 virtual void DrawSegment (const StyledTextImager* imager, const StyleRunElement& runElement, Tablet* tablet, size_t from, size_t to,
299 const TextLayoutBlock& text, const Led_Rect& drawInto, const Led_Rect& /*invalidRect*/,
300 CoordinateType useBaseLine, DistanceType* pixelsDrawn) override;
301 virtual void MeasureSegmentWidth (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to,
302 const Led_tChar* text, DistanceType* distanceResults) const override;
303 virtual DistanceType MeasureSegmentHeight (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to) const override;
304 virtual DistanceType MeasureSegmentBaseLine (const StyledTextImager* imager, const StyleRunElement& runElement, size_t from, size_t to) const override;
305#endif
306 };
307
308 /*
309 @CLASS: SimpleStyleMarkerWithLightUnderline<BASECLASS>
310 @BASES: BASECLASS
311 @DESCRIPTION: <p>Very frequently, you will want to implement a style-marker which just draws some
312 extra stuff drawn at the end.</p>
313 <p>This template is typically used with the default BASECLASS of @'SimpleStyleMarkerWithExtraDraw<BASECLASS>'.</p>
314 */
315 template <class BASECLASS = SimpleStyleMarkerWithExtraDraw<StyleMarker>>
316 class SimpleStyleMarkerWithLightUnderline : public BASECLASS {
317 private:
318 using inherited = BASECLASS;
319
320 public:
321 SimpleStyleMarkerWithLightUnderline () = default;
322
323#if qStroika_Frameworks_Led_SupportGDI
324 protected:
325 virtual void DrawExtra (const StyledTextImager* imager, const StyleRunElement& runElement, Tablet* tablet, size_t from, size_t to,
326 const TextLayoutBlock& text, const Led_Rect& drawInto, CoordinateType useBaseLine, DistanceType pixelsDrawn) override;
327#endif
328
329 public:
330 virtual Color GetUnderlineBaseColor () const;
331 };
332
333#if qStroika_Frameworks_Led_SupportGDI
334 /*
335 @CLASS: StyledTextImager
336 @BASES: virtual @'TextImager'
337 @DESCRIPTION: <p>The class StyledTextImager is a @'TextImager' which knows about special markers,
338 either owned by itself, or the TextStore, which it uses to render and
339 measure the text. It is intended that these special markers it uses to
340 render the text (@'StyledTextImager::StyleMarker''s) be general enough to support both standard
341 style runs, as well as other fancier text adornments, like pictures, opendoc
342 embeddings, etc.</p>
343 <p>You can add arbitrary, and overlapping StyleMarkers to this class, and it will
344 simply render them. Since it must pick ONE StyleMarker to ask todo the drawing,
345 it asks the one with the highest priority (@'StyledTextImager::StyleMarker::GetPriority' ()). If you
346 have some style marker which is smart enuf to pay attention to the
347 overlapping of markers (RARE - IF EVER) it is up to your marker to find which
348 other markers it overlaps with, and handle this combination itself.</p>
349 <p>This class is intended to make easy things like wrapping keywords with little
350 markers which affect how they are displayed. It is ideal for something like
351 a programming text editor which colors keywords, or a typical web browser
352 that has to keep associated links with parts of the text anyhow (keep it in a
353 marker that subclasses from SytleMarker, and then change the color, or font of
354 your display).</p>
355 <p>For the more conventional Style-Run type API, see the class @'StandardStyledTextImager'.</p>
356 */
357 class StyledTextImager : public virtual TextImager {
358 private:
359 using inherited = TextImager;
360
361 protected:
362 StyledTextImager () = default;
363
364 protected:
365 virtual vector<StyleRunElement> SummarizeStyleMarkers (size_t from, size_t to) const;
366 virtual vector<StyleRunElement> SummarizeStyleMarkers (size_t from, size_t to, const TextLayoutBlock& text) const;
367
368 protected:
369 // Must OVERRIDE Draw/Measure text routines so style runs get hooked in and have some effect
370 // when this class is mixed in.
371 virtual void DrawSegment (Tablet* tablet, size_t from, size_t to, const TextLayoutBlock& text, const Led_Rect& drawInto,
372 const Led_Rect& invalidRect, CoordinateType useBaseLine, DistanceType* pixelsDrawn) override;
373 virtual void MeasureSegmentWidth (size_t from, size_t to, const Led_tChar* text, DistanceType* distanceResults) const override;
374 virtual DistanceType MeasureSegmentHeight (size_t from, size_t to) const override;
375 virtual DistanceType MeasureSegmentBaseLine (size_t from, size_t to) const override;
376
377 // Debug support
378 public:
379 nonvirtual void Invariant () const;
380#if qStroika_Foundation_Debug_AssertionsChecked
381 protected:
382 virtual void Invariant_ () const;
383#endif
384 };
385
386#endif
387
388}
389
390/*
391 ********************************************************************************
392 ***************************** Implementation Details ***************************
393 ********************************************************************************
394 */
395#include "StyledTextImager.inl"
396
397#endif /*_Stroika_Frameworks_Led_StyledTextImager_h_*/