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