File indexing completed on 2024-04-21 03:57:34

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2007 Mirko Stocker <me@misto.ch>
0004     SPDX-FileCopyrightText: 2003-2005 Hamish Rodda <rodda@kde.org>
0005     SPDX-FileCopyrightText: 2001 Christoph Cullmann <cullmann@kde.org>
0006     SPDX-FileCopyrightText: 2001 Joseph Wenninger <jowenn@kde.org>
0007     SPDX-FileCopyrightText: 1999 Jochen Wilhelmy <digisnap@cs.tu-berlin.de>
0008 
0009     SPDX-License-Identifier: LGPL-2.0-only
0010 */
0011 
0012 #ifndef KATE_RENDERER_H
0013 #define KATE_RENDERER_H
0014 
0015 #include "kateconfig.h"
0016 #include "ktexteditor/range.h"
0017 
0018 #include <QFlags>
0019 #include <QFont>
0020 #include <QFontMetricsF>
0021 #include <QTextLine>
0022 
0023 namespace KTextEditor
0024 {
0025 class DocumentPrivate;
0026 class ViewPrivate;
0027 class Attribute;
0028 }
0029 class KateRendererConfig;
0030 namespace Kate
0031 {
0032 class TextFolding;
0033 class TextLine;
0034 }
0035 
0036 class KateTextLayout;
0037 class KateLineLayout;
0038 typedef QExplicitlySharedDataPointer<KTextEditor::Attribute> AttributePtr;
0039 
0040 namespace KTextEditor
0041 {
0042 enum class caretStyles {
0043     Line,
0044     Block,
0045     Underline,
0046     Half,
0047 };
0048 }
0049 
0050 /**
0051  * Handles all of the work of rendering the text
0052  * (used for the views and printing)
0053  *
0054  **/
0055 class KateRenderer
0056 {
0057 public:
0058     /**
0059      * Style of Caret
0060      *
0061      * The caret is displayed as a vertical bar (Line), a filled box
0062      * (Block), a horizontal bar (Underline), or a half-height filled
0063      * box (Half). The default is Line.
0064      *
0065      *     Line           Block          Underline           Half
0066      *
0067      * ##     _         #########              _                _
0068      * ##  __| |        #####| |#           __| |            __| |
0069      * ## / _' |        ##/ _' |#          / _' |           / _' |
0070      * ##| (_| |        #| (#| |#         | (_| |         #| (#| |#
0071      * ## \__,_|        ##\__,_|#          \__,_|         ##\__,_|#
0072      * ##               #########        #########        #########
0073      */
0074 
0075     /**
0076      * Constructor
0077      * @param doc document to render
0078      * @param folding folding information
0079      * @param view view which is output (0 for example for rendering to print)
0080      */
0081     explicit KateRenderer(KTextEditor::DocumentPrivate *doc, Kate::TextFolding &folding, KTextEditor::ViewPrivate *view = nullptr);
0082 
0083     KateRenderer(const KateRenderer &) = delete;
0084     KateRenderer &operator=(const KateRenderer &) = delete;
0085 
0086     /**
0087      * Returns the document to which this renderer is bound
0088      */
0089     KTextEditor::DocumentPrivate *doc() const
0090     {
0091         return m_doc;
0092     }
0093 
0094     /**
0095      * Returns the folding info to which this renderer is bound
0096      * @return folding info
0097      */
0098     Kate::TextFolding &folding() const
0099     {
0100         return m_folding;
0101     }
0102 
0103     /**
0104      * Returns the view to which this renderer is bound
0105      */
0106     KTextEditor::ViewPrivate *view() const
0107     {
0108         return m_view;
0109     }
0110 
0111     /**
0112      * update the highlighting attributes
0113      * (for example after an hl change or after hl config changed)
0114      */
0115     void updateAttributes();
0116 
0117     /**
0118      * Determine whether the caret (text cursor) will be drawn.
0119      * @return should it be drawn?
0120      */
0121     inline bool drawCaret() const
0122     {
0123         return m_drawCaret;
0124     }
0125 
0126     /**
0127      * Set whether the caret (text cursor) will be drawn.
0128      * @param drawCaret should caret be drawn?
0129      */
0130     void setDrawCaret(bool drawCaret);
0131 
0132     /**
0133      * The style of the caret (text cursor) to be painted.
0134      * @return caretStyle
0135      */
0136     inline KTextEditor::caretStyles caretStyle() const
0137     {
0138         return m_caretStyle;
0139     }
0140 
0141     /**
0142      * Set the style of caret to be painted.
0143      * @param style style to set
0144      */
0145     void setCaretStyle(KTextEditor::caretStyles style);
0146 
0147     /**
0148      * Set a \a brush with which to override drawing of the caret.  Set to QColor() to clear.
0149      */
0150     void setCaretOverrideColor(const QColor &color);
0151 
0152     /**
0153      * @returns whether tabs should be shown (ie. a small mark
0154      * drawn to identify a tab)
0155      * @return tabs should be shown
0156      */
0157     inline bool showTabs() const
0158     {
0159         return m_showTabs;
0160     }
0161 
0162     /**
0163      * Set whether a mark should be painted to help identifying tabs.
0164      * @param showTabs show the tabs?
0165      */
0166     void setShowTabs(bool showTabs);
0167 
0168     /**
0169      * Set which spaces should be rendered
0170      */
0171     void setShowSpaces(KateDocumentConfig::WhitespaceRendering showSpaces);
0172 
0173     /**
0174      * @returns whether which spaces should be rendered
0175      */
0176     inline KateDocumentConfig::WhitespaceRendering showSpaces() const
0177     {
0178         return m_showSpaces;
0179     }
0180 
0181     /**
0182      * Update marker size shown.
0183      */
0184     void updateMarkerSize();
0185 
0186     /**
0187      * @returns whether non-printable spaces should be shown
0188      */
0189     inline bool showNonPrintableSpaces() const
0190     {
0191         return m_showNonPrintableSpaces;
0192     }
0193 
0194     /**
0195      * Set whether box should be drawn around non-printable spaces
0196      */
0197     void setShowNonPrintableSpaces(const bool showNonPrintableSpaces);
0198 
0199     /**
0200      * Sets the width of the tab. Helps performance.
0201      * @param tabWidth new tab width
0202      */
0203     void setTabWidth(int tabWidth);
0204 
0205     /**
0206      * @returns whether indent lines should be shown
0207      * @return indent lines should be shown
0208      */
0209     bool showIndentLines() const;
0210 
0211     /**
0212      * Set whether a guide should be painted to help identifying indent lines.
0213      * @param showLines show the indent lines?
0214      */
0215     void setShowIndentLines(bool showLines);
0216 
0217     /**
0218      * Sets the width of the tab. Helps performance.
0219      * @param indentWidth new indent width
0220      */
0221     void setIndentWidth(int indentWidth);
0222 
0223     /**
0224      * Show the view's selection?
0225      * @return show sels?
0226      */
0227     inline bool showSelections() const
0228     {
0229         return m_showSelections;
0230     }
0231 
0232     /**
0233      * Set whether the view's selections should be shown.
0234      * The default is true.
0235      * @param showSelections show the selections?
0236      */
0237     void setShowSelections(bool showSelections);
0238 
0239     /**
0240      * Change to a different font (soon to be font set?)
0241      */
0242     void increaseFontSizes(qreal step = 1.0) const;
0243     void decreaseFontSizes(qreal step = 1.0) const;
0244     void resetFontSizes() const;
0245 
0246     /**
0247      * Access currently used font.
0248      * @return current font
0249      */
0250     const QFont &currentFont() const
0251     {
0252         return m_font;
0253     }
0254 
0255     /**
0256      * Access currently used font metrics.
0257      * @return current font metrics
0258      */
0259     const QFontMetricsF &currentFontMetrics() const
0260     {
0261         return m_fontMetrics;
0262     }
0263 
0264     /**
0265      * @return whether the renderer is configured to paint in a
0266      * printer-friendly fashion.
0267      */
0268     bool isPrinterFriendly() const;
0269 
0270     /**
0271      * Configure this renderer to paint in a printer-friendly fashion.
0272      *
0273      * Sets the other options appropriately if true.
0274      */
0275     void setPrinterFriendly(bool printerFriendly);
0276 
0277     /**
0278      * Text width & height calculation functions...
0279      */
0280     void layoutLine(KateLineLayout *line, int maxwidth = -1, bool cacheLayout = false) const;
0281 
0282     /**
0283      * This is a smaller QString::isRightToLeft(). It's also marked as internal to kate
0284      * instead of internal to Qt, so we can modify. This method searches for the first
0285      * strong character in the paragraph and then returns its direction. In case of a
0286      * line without any strong characters, the direction is forced to be LTR.
0287      *
0288      * Back in KDE 4.1 this method counted chars, which lead to unwanted side effects.
0289      * (see https://bugs.kde.org/show_bug.cgi?id=178594). As this function is internal
0290      * the way it work will probably change between releases. Be warned!
0291      */
0292     static bool isLineRightToLeft(QStringView str);
0293 
0294     /**
0295      * The ultimate decoration creation function.
0296      *
0297      * \param selectionsOnly return decorations for selections and/or dynamic highlighting.
0298      */
0299     QList<QTextLayout::FormatRange> decorationsForLine(const Kate::TextLine &textLine, int line, bool selectionsOnly = false) const;
0300 
0301     // Width calculators
0302     qreal spaceWidth() const;
0303 
0304     /**
0305      * Returns the x position of cursor \p col on the line \p range.
0306      */
0307     int cursorToX(const KateTextLayout &range, int col, bool returnPastLine = false) const;
0308     /// \overload
0309     int cursorToX(const KateTextLayout &range, const KTextEditor::Cursor pos, bool returnPastLine = false) const;
0310 
0311     /**
0312      * Returns the real cursor which is occupied by the specified x value, or that closest to it.
0313      * If \p returnPastLine is true, the column will be extrapolated out with the assumption
0314      * that the extra characters are spaces.
0315      */
0316     KTextEditor::Cursor xToCursor(const KateTextLayout &range, int x, bool returnPastLine = false) const;
0317 
0318     // Font height
0319     uint fontHeight() const;
0320 
0321     // Line height
0322     int lineHeight() const;
0323 
0324     // Document height
0325     uint documentHeight() const;
0326 
0327     // Selection boundaries
0328     bool getSelectionBounds(int line, int lineLength, int &start, int &end) const;
0329 
0330     /**
0331      * Flags to customize the paintTextLine function behavior
0332      */
0333     enum PaintTextLineFlag {
0334         /**
0335          * Skip drawing the dashed underline at the start of a folded block of text?
0336          */
0337         SkipDrawFirstInvisibleLineUnderlined = 0x1,
0338         /**
0339          * Skip drawing the line selection
0340          * This is useful when we are drawing the draggable pixmap for drag event
0341          */
0342         SkipDrawLineSelection = 0x2
0343     };
0344     Q_DECLARE_FLAGS(PaintTextLineFlags, PaintTextLineFlag)
0345 
0346     /**
0347      * This is the ultimate function to perform painting of a text line.
0348      *
0349      * The text line is painted from the upper limit of (0,0).  To move that,
0350      * apply a transform to your painter.
0351      *
0352      * @param paint           painter to use
0353      * @param range           layout to use in painting this line
0354      * @param textClipRect    clip rect for text to not paint lines outside the visible area.
0355      * @param xStart          starting width in pixels.
0356      * @param xEnd            ending width in pixels.
0357      * @param cursor          position of the caret, if placed on the current line.
0358      * @param flags           flags for customizing the drawing of the line
0359      */
0360     void paintTextLine(QPainter &paint,
0361                        KateLineLayout *range,
0362                        int xStart,
0363                        int xEnd,
0364                        const QRectF &textClipRect = QRectF(),
0365                        const KTextEditor::Cursor *cursor = nullptr,
0366                        PaintTextLineFlags flags = PaintTextLineFlags());
0367 
0368     /**
0369      * Paint the background of a line
0370      *
0371      * Split off from the main @ref paintTextLine method to make it smaller. As it's being
0372      * called only once per line it shouldn't noticably affect performance and it
0373      * helps readability a LOT.
0374      *
0375      * @param paint           painter to use
0376      * @param layout          layout to use in painting this line
0377      * @param currentViewLine if one of the view lines is the current line, set
0378      *                        this to the index; otherwise -1.
0379      * @param xStart          starting width in pixels.
0380      * @param xEnd            ending width in pixels.
0381      */
0382     void paintTextLineBackground(QPainter &paint, KateLineLayout *layout, int currentViewLine, int xStart, int xEnd);
0383 
0384     void paintTextBackground(QPainter &paint, KateLineLayout *layout, const QList<QTextLayout::FormatRange> &selRanges, const QBrush &br) const;
0385 
0386     /**
0387      * This takes an in index, and returns all the attributes for it.
0388      * For example, if you have a ktextline, and want the KTextEditor::Attribute
0389      * for a given position, do:
0390      *
0391      *   attribute(myktextline->attribute(position));
0392      */
0393     const AttributePtr &attribute(uint pos) const;
0394     AttributePtr specificAttribute(int context) const;
0395 
0396     /**
0397      * Paints a range of text into @a d. This function is mainly used to paint the pixmap
0398      * when dragging text.
0399      *
0400      * Please note that this will not paint the selection background but only the text.
0401      *
0402      * @param d                 the paint device
0403      * @param startLine         start line
0404      * @param xStart            start pos on @a startLine in pixels
0405      * @param endLine           end line
0406      * @param xEnd              end pos on @a endLine in pixels
0407      * @param scale             the amount of scaling to apply. Default is 1.0, negative values are not supported
0408      */
0409     void paintSelection(QPaintDevice *d, int startLine, int xStart, int endLine, int xEnd, qreal scale = 1.0);
0410 
0411 private:
0412     /**
0413      * Paint a trailing space on position (x, y).
0414      */
0415     void paintSpaces(QPainter &paint, const QPointF *points, const int count) const;
0416     /**
0417      * Paint a tab stop marker on position (x, y).
0418      */
0419     void paintTabstop(QPainter &paint, qreal x, qreal y) const;
0420 
0421     /**
0422      * Paint a non-breaking space marker on position (x, y).
0423      */
0424     void paintNonBreakSpace(QPainter &paint, qreal x, qreal y) const;
0425 
0426     /**
0427      * Paint non printable spaces bounding box
0428      */
0429     void paintNonPrintableSpaces(QPainter &paint, qreal x, qreal y, const QChar &chr);
0430 
0431     /** Paint a SciTE-like indent marker. */
0432     void paintIndentMarker(QPainter &paint, uint x, int line);
0433 
0434     static void assignSelectionBrushesFromAttribute(QTextLayout::FormatRange &target, const KTextEditor::Attribute &attribute);
0435 
0436     void paintCaret(KTextEditor::Cursor cursor, KateLineLayout *range, QPainter &paint, int xStart, int xEnd);
0437 
0438     // update font height
0439     void updateFontHeight();
0440 
0441     bool hasCustomLineHeight() const;
0442 
0443     KTextEditor::DocumentPrivate *const m_doc;
0444     Kate::TextFolding &m_folding;
0445     KTextEditor::ViewPrivate *const m_view;
0446 
0447     // cache of config values
0448     int m_tabWidth;
0449     int m_indentWidth;
0450     int m_fontHeight;
0451     float m_fontAscent;
0452 
0453     // if we are at bracket, this will have the X for the opener
0454     int m_currentBracketX = -1;
0455 
0456     // The bracket range, if we are at a bracket
0457     KTextEditor::Range m_currentBracketRange = KTextEditor::Range::invalid();
0458 
0459     // some internal flags
0460     KTextEditor::caretStyles m_caretStyle;
0461     bool m_drawCaret;
0462     bool m_showSelections;
0463     bool m_showTabs;
0464     KateDocumentConfig::WhitespaceRendering m_showSpaces = KateDocumentConfig::None;
0465     float m_markerSize;
0466     bool m_showNonPrintableSpaces;
0467     bool m_printerFriendly;
0468     QColor m_caretOverrideColor;
0469 
0470     QList<AttributePtr> m_attributes;
0471 
0472     /**
0473      * Configuration
0474      */
0475 public:
0476     void updateConfig();
0477 
0478     inline KateRendererConfig *config() const
0479     {
0480         return m_config.get();
0481     }
0482 
0483 private:
0484     std::unique_ptr<KateRendererConfig> const m_config;
0485 
0486     /**
0487      * cached font, was perhaps adjusted for current DPIs
0488      */
0489     QFont m_font;
0490 
0491     /**
0492      * cached font metrics
0493      */
0494     QFontMetricsF m_fontMetrics;
0495 };
0496 
0497 #endif