1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch>
4 SPDX-FileCopyrightText: 2003-2005 Hamish Rodda <rodda@kde.org>
5 SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
6 SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
7 SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
8
9 SPDX-License-Identifier: LGPL-2.0-only
10*/
11
12#ifndef KATE_RENDERER_H
13#define KATE_RENDERER_H
14
15#include "kateconfig.h"
16#include "ktexteditor/range.h"
17
18#include <QFlags>
19#include <QFont>
20#include <QFontMetricsF>
21#include <QTextLine>
22
23namespace KTextEditor
24{
25class DocumentPrivate;
26class ViewPrivate;
27class Attribute;
28}
29class KateRendererConfig;
30namespace Kate
31{
32class TextFolding;
33class TextLine;
34}
35
36class KateTextLayout;
37class KateLineLayout;
38typedef QExplicitlySharedDataPointer<KTextEditor::Attribute> AttributePtr;
39
40namespace KTextEditor
41{
42enum class caretStyles {
43 Line,
44 Block,
45 Underline,
46 Half,
47};
48}
49
50/**
51 * Handles all of the work of rendering the text
52 * (used for the views and printing)
53 *
54 **/
55class KateRenderer
56{
57public:
58 /**
59 * Style of Caret
60 *
61 * The caret is displayed as a vertical bar (Line), a filled box
62 * (Block), a horizontal bar (Underline), or a half-height filled
63 * box (Half). The default is Line.
64 *
65 * Line Block Underline Half
66 *
67 * ## _ ######### _ _
68 * ## __| | #####| |# __| | __| |
69 * ## / _' | ##/ _' |# / _' | / _' |
70 * ##| (_| | #| (#| |# | (_| | #| (#| |#
71 * ## \__,_| ##\__,_|# \__,_| ##\__,_|#
72 * ## ######### ######### #########
73 */
74
75 /**
76 * Constructor
77 * @param doc document to render
78 * @param folding folding information
79 * @param view view which is output (0 for example for rendering to print)
80 */
81 explicit KateRenderer(KTextEditor::DocumentPrivate *doc, Kate::TextFolding &folding, KTextEditor::ViewPrivate *view = nullptr);
82
83 KateRenderer(const KateRenderer &) = delete;
84 KateRenderer &operator=(const KateRenderer &) = delete;
85
86 /**
87 * Returns the document to which this renderer is bound
88 */
89 KTextEditor::DocumentPrivate *doc() const
90 {
91 return m_doc;
92 }
93
94 /**
95 * Returns the folding info to which this renderer is bound
96 * @return folding info
97 */
98 Kate::TextFolding &folding() const
99 {
100 return m_folding;
101 }
102
103 /**
104 * Returns the view to which this renderer is bound
105 */
106 KTextEditor::ViewPrivate *view() const
107 {
108 return m_view;
109 }
110
111 /**
112 * update the highlighting attributes
113 * (for example after an hl change or after hl config changed)
114 */
115 void updateAttributes();
116
117 /**
118 * Determine whether the caret (text cursor) will be drawn.
119 * @return should it be drawn?
120 */
121 inline bool drawCaret() const
122 {
123 return m_drawCaret;
124 }
125
126 /**
127 * Set whether the caret (text cursor) will be drawn.
128 * @param drawCaret should caret be drawn?
129 */
130 void setDrawCaret(bool drawCaret);
131
132 /**
133 * The style of the caret (text cursor) to be painted.
134 * @return caretStyle
135 */
136 inline KTextEditor::caretStyles caretStyle() const
137 {
138 return m_caretStyle;
139 }
140
141 /**
142 * Set the style of caret to be painted.
143 * @param style style to set
144 */
145 void setCaretStyle(KTextEditor::caretStyles style);
146
147 /**
148 * Set a \a brush with which to override drawing of the caret. Set to QColor() to clear.
149 */
150 void setCaretOverrideColor(const QColor &color);
151
152 /**
153 * @returns whether tabs should be shown (ie. a small mark
154 * drawn to identify a tab)
155 * @return tabs should be shown
156 */
157 inline bool showTabs() const
158 {
159 return m_showTabs;
160 }
161
162 /**
163 * Set whether a mark should be painted to help identifying tabs.
164 * @param showTabs show the tabs?
165 */
166 void setShowTabs(bool showTabs);
167
168 /**
169 * Set which spaces should be rendered
170 */
171 void setShowSpaces(KateDocumentConfig::WhitespaceRendering showSpaces);
172
173 /**
174 * @returns whether which spaces should be rendered
175 */
176 inline KateDocumentConfig::WhitespaceRendering showSpaces() const
177 {
178 return m_showSpaces;
179 }
180
181 /**
182 * Update marker size shown.
183 */
184 void updateMarkerSize();
185
186 /**
187 * @returns whether non-printable spaces should be shown
188 */
189 inline bool showNonPrintableSpaces() const
190 {
191 return m_showNonPrintableSpaces;
192 }
193
194 /**
195 * Set whether box should be drawn around non-printable spaces
196 */
197 void setShowNonPrintableSpaces(const bool showNonPrintableSpaces);
198
199 /**
200 * Sets the width of the tab. Helps performance.
201 * @param tabWidth new tab width
202 */
203 void setTabWidth(int tabWidth);
204
205 /**
206 * @returns whether indent lines should be shown
207 * @return indent lines should be shown
208 */
209 bool showIndentLines() const;
210
211 /**
212 * Set whether a guide should be painted to help identifying indent lines.
213 * @param showLines show the indent lines?
214 */
215 void setShowIndentLines(bool showLines);
216
217 /**
218 * Sets the width of the tab. Helps performance.
219 * @param indentWidth new indent width
220 */
221 void setIndentWidth(int indentWidth);
222
223 /**
224 * Show the view's selection?
225 * @return show sels?
226 */
227 inline bool showSelections() const
228 {
229 return m_showSelections;
230 }
231
232 /**
233 * Set whether the view's selections should be shown.
234 * The default is true.
235 * @param showSelections show the selections?
236 */
237 void setShowSelections(bool showSelections);
238
239 /**
240 * Change to a different font (soon to be font set?)
241 */
242 void increaseFontSizes(qreal step = 1.0) const;
243 void decreaseFontSizes(qreal step = 1.0) const;
244 void resetFontSizes() const;
245
246 /**
247 * Access currently used font.
248 * @return current font
249 */
250 const QFont &currentFont() const
251 {
252 return m_font;
253 }
254
255 /**
256 * Access currently used font metrics.
257 * @return current font metrics
258 */
259 const QFontMetricsF &currentFontMetrics() const
260 {
261 return m_fontMetrics;
262 }
263
264 /**
265 * @return whether the renderer is configured to paint in a
266 * printer-friendly fashion.
267 */
268 bool isPrinterFriendly() const;
269
270 /**
271 * Configure this renderer to paint in a printer-friendly fashion.
272 *
273 * Sets the other options appropriately if true.
274 */
275 void setPrinterFriendly(bool printerFriendly);
276
277 /**
278 * Text width & height calculation functions...
279 */
280 void layoutLine(KateLineLayout *line, int maxwidth = -1, bool cacheLayout = false) const;
281
282 /**
283 * This is a smaller QString::isRightToLeft(). It's also marked as internal to kate
284 * instead of internal to Qt, so we can modify. This method searches for the first
285 * strong character in the paragraph and then returns its direction. In case of a
286 * line without any strong characters, the direction is forced to be LTR.
287 *
288 * Back in KDE 4.1 this method counted chars, which lead to unwanted side effects.
289 * (see https://bugs.kde.org/show_bug.cgi?id=178594). As this function is internal
290 * the way it work will probably change between releases. Be warned!
291 */
292 static bool isLineRightToLeft(QStringView str);
293
294 /**
295 * The ultimate decoration creation function.
296 *
297 * \param selectionsOnly return decorations for selections and/or dynamic highlighting.
298 */
299 QList<QTextLayout::FormatRange> decorationsForLine(const Kate::TextLine &textLine, int line, bool selectionsOnly = false) const;
300
301 // Width calculators
302 qreal spaceWidth() const;
303
304 /**
305 * Returns the x position of cursor \p col on the line \p range.
306 */
307 int cursorToX(const KateTextLayout &range, int col, bool returnPastLine = false) const;
308 /// \overload
309 int cursorToX(const KateTextLayout &range, const KTextEditor::Cursor pos, bool returnPastLine = false) const;
310
311 /**
312 * Returns the real cursor which is occupied by the specified x value, or that closest to it.
313 * If \p returnPastLine is true, the column will be extrapolated out with the assumption
314 * that the extra characters are spaces.
315 */
316 KTextEditor::Cursor xToCursor(const KateTextLayout &range, int x, bool returnPastLine = false) const;
317
318 // Font height
319 uint fontHeight() const;
320
321 // Line height
322 int lineHeight() const;
323
324 // Document height
325 uint documentHeight() const;
326
327 // Selection boundaries
328 bool getSelectionBounds(int line, int lineLength, int &start, int &end) const;
329
330 /**
331 * Flags to customize the paintTextLine function behavior
332 */
333 enum PaintTextLineFlag {
334 /**
335 * Skip drawing the dashed underline at the start of a folded block of text?
336 */
337 SkipDrawFirstInvisibleLineUnderlined = 0x1,
338 /**
339 * Skip drawing the line selection
340 * This is useful when we are drawing the draggable pixmap for drag event
341 */
342 SkipDrawLineSelection = 0x2
343 };
344 Q_DECLARE_FLAGS(PaintTextLineFlags, PaintTextLineFlag)
345
346 /**
347 * This is the ultimate function to perform painting of a text line.
348 *
349 * The text line is painted from the upper limit of (0,0). To move that,
350 * apply a transform to your painter.
351 *
352 * @param paint painter to use
353 * @param range layout to use in painting this line
354 * @param textClipRect clip rect for text to not paint lines outside the visible area.
355 * @param xStart starting width in pixels.
356 * @param xEnd ending width in pixels.
357 * @param cursor position of the caret, if placed on the current line.
358 * @param flags flags for customizing the drawing of the line
359 */
360 void paintTextLine(QPainter &paint,
361 KateLineLayout *range,
362 int xStart,
363 int xEnd,
364 const QRectF &textClipRect = QRectF(),
365 const KTextEditor::Cursor *cursor = nullptr,
366 PaintTextLineFlags flags = PaintTextLineFlags());
367
368 /**
369 * Paint the background of a line
370 *
371 * Split off from the main @ref paintTextLine method to make it smaller. As it's being
372 * called only once per line it shouldn't noticably affect performance and it
373 * helps readability a LOT.
374 *
375 * @param paint painter to use
376 * @param layout layout to use in painting this line
377 * @param currentViewLine if one of the view lines is the current line, set
378 * this to the index; otherwise -1.
379 * @param xStart starting width in pixels.
380 * @param xEnd ending width in pixels.
381 */
382 void paintTextLineBackground(QPainter &paint, KateLineLayout *layout, int currentViewLine, int xStart, int xEnd);
383
384 void paintTextBackground(QPainter &paint, KateLineLayout *layout, const QList<QTextLayout::FormatRange> &selRanges, const QBrush &br, int xStart) const;
385
386 /**
387 * This takes an in index, and returns all the attributes for it.
388 * For example, if you have a ktextline, and want the KTextEditor::Attribute
389 * for a given position, do:
390 *
391 * attribute(myktextline->attribute(position));
392 */
393 const AttributePtr &attribute(uint pos) const;
394 AttributePtr specificAttribute(int context) const;
395
396 /**
397 * Paints a range of text into @a d. This function is mainly used to paint the pixmap
398 * when dragging text.
399 *
400 * Please note that this will not paint the selection background but only the text.
401 *
402 * @param d the paint device
403 * @param startLine start line
404 * @param xStart start pos on @a startLine in pixels
405 * @param endLine end line
406 * @param xEnd end pos on @a endLine in pixels
407 * @param scale the amount of scaling to apply. Default is 1.0, negative values are not supported
408 */
409 void paintSelection(QPaintDevice *d, int startLine, int xStart, int endLine, int xEnd, qreal scale = 1.0);
410
411private:
412 /**
413 * Paint a trailing space on position (x, y).
414 */
415 void paintSpaces(QPainter &paint, const QPointF *points, const int count) const;
416 /**
417 * Paint a tab stop marker on position (x, y).
418 */
419 void paintTabstop(QPainter &paint, qreal x, qreal y) const;
420
421 /**
422 * Paint a non-breaking space marker on position (x, y).
423 */
424 void paintNonBreakSpace(QPainter &paint, qreal x, qreal y) const;
425
426 /**
427 * Paint non printable spaces bounding box
428 */
429 void paintNonPrintableSpaces(QPainter &paint, qreal x, qreal y, const QChar &chr);
430
431 /** Paint a SciTE-like indent marker. */
432 void paintIndentMarker(QPainter &paint, uint x, int line);
433
434 static void assignSelectionBrushesFromAttribute(QTextLayout::FormatRange &target, const KTextEditor::Attribute &attribute);
435
436 void paintCaret(KTextEditor::Cursor cursor, KateLineLayout *range, QPainter &paint, int xStart, int xEnd);
437
438 // update font height
439 void updateFontHeight();
440
441 bool hasCustomLineHeight() const;
442
443 KTextEditor::DocumentPrivate *const m_doc;
444 Kate::TextFolding &m_folding;
445 KTextEditor::ViewPrivate *const m_view;
446
447 // cache of config values
448 int m_tabWidth;
449 int m_indentWidth;
450 int m_fontHeight;
451 float m_fontAscent;
452
453 // if we are at bracket, this will have the X for the opener
454 int m_currentBracketX = -1;
455
456 // The bracket range, if we are at a bracket
457 KTextEditor::Range m_currentBracketRange = KTextEditor::Range::invalid();
458
459 // some internal flags
460 KTextEditor::caretStyles m_caretStyle;
461 bool m_drawCaret;
462 bool m_showSelections;
463 bool m_showTabs;
464 KateDocumentConfig::WhitespaceRendering m_showSpaces = KateDocumentConfig::None;
465 float m_markerSize;
466 bool m_showNonPrintableSpaces;
467 bool m_printerFriendly;
468 QColor m_caretOverrideColor;
469
470 QList<AttributePtr> m_attributes;
471
472 /**
473 * Configuration
474 */
475public:
476 void updateConfig();
477
478 inline KateRendererConfig *config() const
479 {
480 return m_config.get();
481 }
482
483private:
484 std::unique_ptr<KateRendererConfig> const m_config;
485
486 /**
487 * cached font, was perhaps adjusted for current DPIs
488 */
489 QFont m_font;
490
491 /**
492 * cached font metrics
493 */
494 QFontMetricsF m_fontMetrics;
495};
496
497#endif
498

source code of ktexteditor/src/render/katerenderer.h