diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,6 +146,7 @@ view/kateview.cpp view/kateviewinternal.cpp view/kateviewhelpers.cpp +view/kateannotationitemdelegate.cpp view/katemessagewidget.cpp view/katefadeeffect.cpp view/kateanimation.cpp diff --git a/src/include/CMakeLists.txt b/src/include/CMakeLists.txt --- a/src/include/CMakeLists.txt +++ b/src/include/CMakeLists.txt @@ -3,6 +3,7 @@ HEADER_NAMES AnnotationInterface CodeCompletionModelControllerInterface MovingCursor Range TextHintInterface Cursor MarkInterface MovingInterface InlineNote InlineNoteProvider InlineNoteInterface + AbstractAnnotationItemDelegate Document MovingRange View Attribute Command DocumentCursor Message MovingRangeFeedback SessionConfigInterface CodeCompletionInterface ConfigInterface Editor diff --git a/src/include/ktexteditor/abstractannotationitemdelegate.h b/src/include/ktexteditor/abstractannotationitemdelegate.h new file mode 100644 --- /dev/null +++ b/src/include/ktexteditor/abstractannotationitemdelegate.h @@ -0,0 +1,211 @@ +/* This file is part of the KDE libraries + * Copyright 2017-2018 Friedrich W. H. Kossebau + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#ifndef KTEXTEDITOR_ABSTRACTANNOTATIONITEMDELEGATE_H +#define KTEXTEDITOR_ABSTRACTANNOTATIONITEMDELEGATE_H + +#include + +#include +#include + +class QHelpEvent; +class QPoint; + + +namespace KTextEditor +{ +class AnnotationModel; +class View; + +/** + * \brief The style option set for an annotation item, as painted by AbstractAnnotationItemDelegate + * + * \since 5.53 + * \see KTextEditor::AbstractAnnotationItemDelegate + */ +class KTEXTEDITOR_EXPORT StyleOptionAnnotationItem : public QStyleOption +{ +public: + // TODO: not sure what SO_Default implies, but no clue how to maintain a user type registry? + enum StyleOptionType { Type = SO_Default }; + enum StyleOptionVersion { Version = 1 }; + + /** + * Index of the displayed line in the wrapped lines for the given real line + */ + int wrappedLine = 0; + /** + * Number of wrapped lines for the given real line + * + * A value of 1 means no wrapping has happened and the real line is displayed as one line. + */ + int wrappedLineCount = 1; + /** + * Index of the displayed line in the displayed lines for the same group + */ + int visibleWrappedLineInGroup = 0; + + /** + * The view where the annotation is shown + * + * There is always a view set. + */ + KTextEditor::View* view = nullptr; + /** + * Recommended size for icons or other symbols that will be rendered by the delegate + * + * The default value is QSize(-1, -1). + */ + QSize decorationSize; + /** + * The metrics of the font used for rendering the text document + */ + QFontMetricsF contentFontMetrics; + + /** + * Enum for describing the relative position of a real line in the row of consecutive + * displayed lines which belong to the same group of annotation items + */ + enum AnnotationItemGroupPosition { + InvalidGroupPosition = 0, ///< Position not specified or not belonging to a group + InGroup = 0x1 << 0, ///< Real line belongs to a group + GroupBegin = 0x1 << 1, ///< Real line is first of consecutive lines from same group + GroupEnd = 0x1 << 2, ///< Real line is last of consecutive lines from same group + }; + Q_DECLARE_FLAGS(AnnotationItemGroupPositions, AnnotationItemGroupPosition) + + /** + * Relative position of the real line in the row of consecutive displayed lines + * which belong to the same group of annotation items + */ + AnnotationItemGroupPositions annotationItemGroupingPosition = InvalidGroupPosition; + +public: + StyleOptionAnnotationItem(); + StyleOptionAnnotationItem(const StyleOptionAnnotationItem &other); + +protected: + explicit StyleOptionAnnotationItem(int version); +}; + + +/** + * \brief A delegate for rendering line annotation information and handling events + * + * \section annodelegate_intro Introduction + * + * AbstractAnnotationItemDelegate is a base class that can be reimplemented + * to customize the rendering of annotation information for each line in a document. + * It provides also the hooks to define handling of help events like tooltip or of + * the request for a context menu. + * + * \section annodelegate_impl Implementing an AbstractAnnotationItemDelegate + * + * The public interface of this class is loosely based on the QAbstractItemDelegate + * interfaces. It has five methods to implement. + * + * \since 5.53 + * \see KTextEditor::AnnotationModel, KTextEditor::AnnotationViewInterfaceV2 + */ +class KTEXTEDITOR_EXPORT AbstractAnnotationItemDelegate : public QObject +{ + Q_OBJECT + +protected: + explicit AbstractAnnotationItemDelegate(QObject *parent = nullptr); + +public: + ~AbstractAnnotationItemDelegate() override; + +public: + /** + * This pure abstract function must be reimplemented to provide custom rendering. + * Use the painter and style option to render the annotation information for the line + * specified by the arguments @p model and @p line. + * @param painter the painter object + * @param option the option object with the info needed for styling + * @param model the annotation model providing the annotation information + * @param line index of the real line the annotation information should be painted for + * + * Reimplement this in line with sizeHint(). + */ + virtual void paint(QPainter *painter, const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) const = 0; + /** + * This pure abstract function must be reimplemented to provide custom rendering. + * Use the style option to calculate the best size for the annotation information + * for the line specified by the arguments @p model and @p line. + * This should be the size for the display for a single displayed content line, + * i.e. with no line wrapping or consecutive multiple annotation item of the same group assumed. + * + * @note If AnnotationViewInterfaceV2::uniformAnnotationItemSizes() is @c true for the view + * this delegate is used by, it is assumed that the returned value is the same for + * any line. + * + * @param option the option object with the info needed for styling + * @param model the annotation model providing the annotation information + * @param line index of the real line the annotation information should be painted for + * @return best size for the annotation information + * + * Reimplement this in line with paint(). + */ + virtual QSize sizeHint(const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) const = 0; + /** + * Whenever a help event occurs, this function is called with the event view option + * and @p model and @p line specifying the item where the event occurs. + * This pure abstract function must be reimplemented to provide custom tooltips. + * @param event the help event + * @param view the view for which the help event is requested + * @param option the style option object with the info needed for styling, including the rect of the annotation + * @param model the annotation model providing the annotation information + * @param line index of the real line the annotation information should be painted for + * @return @c true if the event could be handled (implies that the data obtained from the model had the required role), @c false otherwise + * + * Reimplement this in line with hideTooltip(). + */ + virtual bool helpEvent(QHelpEvent *event, KTextEditor::View *view, + const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) = 0; + /** + * This pure abstract function must be reimplemented to provide custom tooltips. + * It is called whenever a possible still shown tooltip no longer is valid, + * e.g. if the annotations have been hidden. + * @param view the view for which the tooltip was requested + * + * Reimplement this in line with helpEvent(). + */ + virtual void hideTooltip(KTextEditor::View *view) = 0; + +Q_SIGNALS: + /** + * This signal must be emitted when the sizeHint() for @p model and @p line changed. + * The view automatically connects to this signal and relayouts as necessary. + * If AnnotationViewInterfaceV2::uniformAnnotationItemSizes is set on the view, + * it is sufficient to emit sizeHintChanged only for one line. + * @param model the annotation model providing the annotation information + * @param line index of the real line the annotation information should be painted for + */ + void sizeHintChanged(KTextEditor::AnnotationModel *model, int line); +}; + +} + +#endif diff --git a/src/include/ktexteditor/annotationinterface.h b/src/include/ktexteditor/annotationinterface.h --- a/src/include/ktexteditor/annotationinterface.h +++ b/src/include/ktexteditor/annotationinterface.h @@ -1,6 +1,7 @@ /* This file is part of the KDE libraries * Copyright 2008 Andreas Pakulat * Copyright 2008-2018 Dominik Haumann + * Copyright 2017-2018 Friedrich W. H. Kossebau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -31,6 +32,7 @@ { class View; +class AbstractAnnotationItemDelegate; /** * \brief An model for providing line annotation information @@ -60,6 +62,7 @@ enum { GroupIdentifierRole = Qt::UserRole }; + // KF6: add AnnotationModelUserRole = Qt::UserRole + 0x100 /** * data() is used to retrieve the information needed to present the @@ -81,7 +84,7 @@ * * \returns a QVariant that contains the data for the given role. */ - virtual QVariant data(int line, Qt::ItemDataRole role) const = 0; + virtual QVariant data(int line, Qt::ItemDataRole role) const = 0; //KF6: use int for role Q_SIGNALS: /** @@ -270,10 +273,87 @@ }; +/** + * \brief Annotation interface for the View, version 2 + * + * \ingroup kte_group_view_extensions + * + * \section annoview_intro Introduction + * + * The AnnotationViewInterfaceV2 allows to do the same as AnnotationViewInterface + * and additionally + * - (1) set a custom AbstractAnnotationItemDelegate for the View. + * + * For a more detailed explanation about whether you want to set a custom + * delegate for rendering the annotations, read the detailed documentation about the + * AbstractAnnotationItemDelegate. + * + * \section annoview_access Accessing the AnnotationViewInterfaceV2 + * + * The AnnotationViewInterfaceV2 is an extension interface for a + * View, i.e. the View inherits the interface \e provided that the + * used KTextEditor library implements the interface. Use qobject_cast to + * access the interface: + * \code + * // view is of type KTextEditor::View* + * auto iface = qobject_cast(view); + * + * if (iface) { + * // the implementation supports the interface + * // do stuff + * iface->setAnnotationItemDelegate(myDelegate); + * iface->setAnnotationUniformItemSizes(true); + * } else { + * // the implementation does not support the interface + * } + * \endcode + * + * \since 5.53 + */ +class KTEXTEDITOR_EXPORT AnnotationViewInterfaceV2 : public AnnotationViewInterface +{ + // KF6: Merge KTextEditor::AnnotationViewInterfaceV2 into KTextEditor::AnnotationViewInterface (kossebau) +public: + virtual ~AnnotationViewInterfaceV2() {} + + /** + * Sets the AbstractAnnotationItemDelegate for this view and the model + * to provide custom rendering of annotation information for each line. + * Ownership is not transferred. + * + * \param delegate the new AbstractAnnotationItemDelegate, or \c nullptr to reset to the default delegate + */ + virtual void setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate) = 0; + + /** + * Returns the currently used AbstractAnnotationItemDelegate + * + * @returns the current AbstractAnnotationItemDelegate + */ + virtual KTextEditor::AbstractAnnotationItemDelegate* annotationItemDelegate() const = 0; + + /** + * This function can be used to declare whether it is known that the annotation items + * rendered by the set delegate all have the same size. + * This enables the view to do some optimizations for performance purposes. + * + * By default the value of this property is \c false . + * + * @param uniformItemSizes if \c true the annotation items are considered to all have the same size + */ + virtual void setAnnotationUniformItemSizes(bool uniformItemSizes) = 0; + + /** + * Checks whether the annotation items all have the same size. + */ + virtual bool uniformAnnotationItemSizes() const = 0; +}; + } Q_DECLARE_INTERFACE(KTextEditor::AnnotationInterface, "org.kde.KTextEditor.AnnotationInterface") Q_DECLARE_INTERFACE(KTextEditor::AnnotationViewInterface, "org.kde.KTextEditor.AnnotationViewInterface") +Q_DECLARE_INTERFACE(KTextEditor::AnnotationViewInterfaceV2, "org.kde.KTextEditor.AnnotationViewInterfaceV2") #endif diff --git a/src/utils/ktexteditor.cpp b/src/utils/ktexteditor.cpp --- a/src/utils/ktexteditor.cpp +++ b/src/utils/ktexteditor.cpp @@ -42,6 +42,7 @@ #include "texthintinterface.h" #include "annotationinterface.h" +#include "abstractannotationitemdelegate.h" #include "kateglobal.h" #include "kateconfig.h" @@ -375,3 +376,25 @@ { return d->textAreaRectInternal(); } + +StyleOptionAnnotationItem::StyleOptionAnnotationItem() + : contentFontMetrics(QFont()) +{} + +StyleOptionAnnotationItem::StyleOptionAnnotationItem(const StyleOptionAnnotationItem &other) + : QStyleOption(Version, Type) + , contentFontMetrics(QFont()) +{ + *this = other; +} + +StyleOptionAnnotationItem::StyleOptionAnnotationItem(int version) + : QStyleOption(version, Type) + , contentFontMetrics(QFont()) +{} + +AbstractAnnotationItemDelegate::AbstractAnnotationItemDelegate(QObject *parent) + : QObject(parent) +{} + +AbstractAnnotationItemDelegate::~AbstractAnnotationItemDelegate() = default; diff --git a/src/view/kateannotationitemdelegate.h b/src/view/kateannotationitemdelegate.h new file mode 100644 --- /dev/null +++ b/src/view/kateannotationitemdelegate.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017-18 Friedrich W. H. Kossebau + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KATE_ANNOTATIONITEMDELEGATE_H +#define KATE_ANNOTATIONITEMDELEGATE_H + +#include + +namespace KTextEditor { +class ViewPrivate; +} +class KateViewInternal; + +class KateAnnotationItemDelegate : public KTextEditor::AbstractAnnotationItemDelegate +{ + Q_OBJECT + +public: + explicit KateAnnotationItemDelegate(KateViewInternal *internalView, QObject *parent); + ~KateAnnotationItemDelegate() override; + +public: + void paint(QPainter *painter, const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) const override; + bool helpEvent(QHelpEvent *event, KTextEditor::View *view, + const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) override; + void hideTooltip(KTextEditor::View *view) override; + QSize sizeHint(const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) const override; + +private: + KateViewInternal *m_internalView; + KTextEditor::ViewPrivate *m_view; + + mutable qreal m_maxCharWidth = 0.0; + mutable QFontMetricsF m_cachedDataContentFontMetrics; +}; + +#endif diff --git a/src/view/kateannotationitemdelegate.cpp b/src/view/kateannotationitemdelegate.cpp new file mode 100644 --- /dev/null +++ b/src/view/kateannotationitemdelegate.cpp @@ -0,0 +1,162 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017-18 Friedrich W. H. Kossebau + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kateannotationitemdelegate.h" + +#include "kateviewinternal.h" + +#include + +#include +#include +#include + +#include + + +KateAnnotationItemDelegate::KateAnnotationItemDelegate(KateViewInternal *internalView, QObject *parent) + : KTextEditor::AbstractAnnotationItemDelegate(parent) + , m_internalView(internalView) + , m_view(internalView->m_view) + , m_cachedDataContentFontMetrics(QFont()) +{ +} + +KateAnnotationItemDelegate::~KateAnnotationItemDelegate() = default; + +void KateAnnotationItemDelegate::paint(QPainter *painter, const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) const +{ + Q_ASSERT(painter); + Q_ASSERT(model); + if (!painter || !model) { + return; + } + // TODO: also test line for validity for sake of completeness? + + painter->save(); + + const int margin = 3; + + const QVariant background = model->data(line, Qt::BackgroundRole); + // Fill the background + if (background.isValid()) { + painter->fillRect(option.rect, background.value()); + } + + const QVariant foreground = model->data(line, Qt::ForegroundRole); + // Set the pen for drawing the foreground + if (foreground.isValid() && foreground.canConvert()) { + painter->setPen(foreground.value()); + } + + // Draw a border around all adjacent entries that have the same text as the currently hovered one + if ((option.state & QStyle::State_MouseOver) && + (option.annotationItemGroupingPosition & KTextEditor::StyleOptionAnnotationItem::InGroup)) { + // Use floating point coordinates to support scaled rendering + QRectF rect(option.rect); + rect.adjust(0.5, 0.5, -0.5, -0.5); + + // draw left and right highlight borders + painter->drawLine(rect.topLeft(), rect.bottomLeft()); + painter->drawLine(rect.topRight(), rect.bottomRight()); + + if ((option.annotationItemGroupingPosition & KTextEditor::StyleOptionAnnotationItem::GroupBegin) && + (option.wrappedLine == 0)) { + painter->drawLine(rect.topLeft(), rect.topRight()); + } + + if ((option.annotationItemGroupingPosition & KTextEditor::StyleOptionAnnotationItem::GroupEnd) && + (option.wrappedLine == (option.wrappedLineCount-1))) { + painter->drawLine(rect.bottomLeft(), rect.bottomRight()); + } + } + // reset pen + if (foreground.isValid()) { + QPen pen = painter->pen(); + pen.setWidth(1); + painter->setPen(pen); + } + + // Now draw the normal text + const QVariant text = model->data(line, Qt::DisplayRole); + if ((option.wrappedLine == 0) && text.isValid() && text.canConvert()) { + painter->drawText(option.rect.x() + margin, option.rect.y(), + option.rect.width() - 2*margin, option.rect.height(), + Qt::AlignLeft | Qt::AlignVCenter, text.toString()); + } + + painter->restore(); +} + +bool KateAnnotationItemDelegate::helpEvent(QHelpEvent *event, KTextEditor::View *view, + const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) +{ + Q_UNUSED(option); + + if (!model || event->type() != QEvent::ToolTip) { + return false; + } + + const QVariant data = model->data(line, Qt::ToolTipRole); + if (!data.isValid()) { + return false; + } + + const QString toolTipText = data.toString(); + if (toolTipText.isEmpty()) { + return false; + } + + QToolTip::showText(event->globalPos(), toolTipText, view, option.rect); + + return true; +} + +void KateAnnotationItemDelegate::hideTooltip(KTextEditor::View *view) +{ + Q_UNUSED(view); + QToolTip::hideText(); +} + +QSize KateAnnotationItemDelegate::sizeHint(const KTextEditor::StyleOptionAnnotationItem &option, + KTextEditor::AnnotationModel *model, int line) const +{ + Q_ASSERT(model); + if (!model) { + return QSize(0, 0); + } + + // recalculate m_maxCharWidth if needed + if (m_maxCharWidth == 0.0 || (option.contentFontMetrics != m_cachedDataContentFontMetrics)) { + m_maxCharWidth = 0.0; + // based on old code written when just a hash was shown, could see an update + // Loop to determine the widest numeric character in the current font. + for (char c = '0'; c <= '9'; ++c) { + const qreal charWidth = ceil(option.contentFontMetrics.width(QLatin1Char(c))); + m_maxCharWidth = qMax(m_maxCharWidth, charWidth); + } + + m_cachedDataContentFontMetrics = option.contentFontMetrics; + } + + const QString annotationText = model->data(line, Qt::DisplayRole).toString(); + return QSize(annotationText.length() * m_maxCharWidth + 8, option.contentFontMetrics.height()); +} diff --git a/src/view/kateview.h b/src/view/kateview.h --- a/src/view/kateview.h +++ b/src/view/kateview.h @@ -88,13 +88,14 @@ public KTextEditor::CodeCompletionInterface, public KTextEditor::ConfigInterface, public KTextEditor::InlineNoteInterface, - public KTextEditor::AnnotationViewInterface + public KTextEditor::AnnotationViewInterfaceV2 { Q_OBJECT Q_INTERFACES(KTextEditor::TextHintInterface) Q_INTERFACES(KTextEditor::ConfigInterface) Q_INTERFACES(KTextEditor::CodeCompletionInterface) Q_INTERFACES(KTextEditor::AnnotationViewInterface) + Q_INTERFACES(KTextEditor::AnnotationViewInterfaceV2) Q_INTERFACES(KTextEditor::InlineNoteInterface) friend class KTextEditor::View; @@ -369,6 +370,10 @@ KTextEditor::AnnotationModel *annotationModel() const override; void setAnnotationBorderVisible(bool visible) override; bool isAnnotationBorderVisible() const override; + void setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate) override; + KTextEditor::AbstractAnnotationItemDelegate* annotationItemDelegate() const override; + void setAnnotationUniformItemSizes(bool enable) override; + bool uniformAnnotationItemSizes() const override; Q_SIGNALS: void annotationContextMenuAboutToShow(KTextEditor::View *view, QMenu *menu, int line) override; diff --git a/src/view/kateview.cpp b/src/view/kateview.cpp --- a/src/view/kateview.cpp +++ b/src/view/kateview.cpp @@ -3245,17 +3245,33 @@ void KTextEditor::ViewPrivate::setAnnotationBorderVisible(bool visible) { m_viewInternal->m_leftBorder->setAnnotationBorderOn(visible); - if ( !visible ) { - // make sure the tooltip is hidden - QToolTip::hideText(); - } } bool KTextEditor::ViewPrivate::isAnnotationBorderVisible() const { return m_viewInternal->m_leftBorder->annotationBorderOn(); } +KTextEditor::AbstractAnnotationItemDelegate* KTextEditor::ViewPrivate::annotationItemDelegate() const +{ + return m_viewInternal->m_leftBorder->annotationItemDelegate(); +} + +void KTextEditor::ViewPrivate::setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate) +{ + m_viewInternal->m_leftBorder->setAnnotationItemDelegate(delegate); +} + +bool KTextEditor::ViewPrivate::uniformAnnotationItemSizes() const +{ + return m_viewInternal->m_leftBorder->uniformAnnotationItemSizes(); +} + +void KTextEditor::ViewPrivate::setAnnotationUniformItemSizes(bool enable) +{ + m_viewInternal->m_leftBorder->setAnnotationUniformItemSizes(enable); +} + KTextEditor::Range KTextEditor::ViewPrivate::visibleRange() { //ensure that the view is up-to-date, otherwise 'endPos()' might fail! diff --git a/src/view/kateviewhelpers.h b/src/view/kateviewhelpers.h --- a/src/view/kateviewhelpers.h +++ b/src/view/kateviewhelpers.h @@ -2,6 +2,7 @@ Copyright (C) 2002 John Firebaugh Copyright (C) 2001 Anders Lund Copyright (C) 2001 Christoph Cullmann + Copyright 2017-2018 Friedrich W. H. Kossebau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -44,6 +45,7 @@ namespace KTextEditor { class DocumentPrivate; } namespace KTextEditor { class ViewPrivate; } class KateViewInternal; +class KateTextLayout; #define MAXFOLDINGCOLORS 16 @@ -55,6 +57,8 @@ class Command; class AnnotationModel; class MovingRange; +class AbstractAnnotationItemDelegate; +class StyleOptionAnnotationItem; } class QTimer; @@ -289,6 +293,17 @@ enum BorderArea { None, LineNumbers, IconBorder, FoldingMarkers, AnnotationBorder, ModificationBorder }; BorderArea positionToArea(const QPoint &) const; + KTextEditor::AbstractAnnotationItemDelegate* annotationItemDelegate() const; + void setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate); + inline bool uniformAnnotationItemSizes() const + { + return m_hasUniformAnnotationItemSizes; + } + inline void setAnnotationUniformItemSizes(bool enable) + { + m_hasUniformAnnotationItemSizes = enable; + } + public Q_SLOTS: void updateAnnotationBorderWidth(); void updateAnnotationLine(int line); @@ -308,12 +323,20 @@ void showMarkMenu(uint line, const QPoint &pos); - void showAnnotationTooltip(int line, const QPoint &pos); void hideAnnotationTooltip(); void removeAnnotationHovering(); void showAnnotationMenu(int line, const QPoint &pos); - int annotationLineWidth(int line); + void calcAnnotationBorderWidth(); + + void initStyleOption(KTextEditor::StyleOptionAnnotationItem *styleOption) const; + void setStyleOptionLineData(KTextEditor::StyleOptionAnnotationItem *styleOption, + int y, + int realLine, + const KTextEditor::AnnotationModel *model, + const QString &annotationGroupIdentifier) const; + QRect annotationLineRectInView(int line) const; +private: KTextEditor::ViewPrivate *m_view; KTextEditor::DocumentPrivate *m_doc; KateViewInternal *m_viewInternal; @@ -335,6 +358,10 @@ int iconPaneWidth; int m_annotationBorderWidth; + KTextEditor::AbstractAnnotationItemDelegate *m_annotationItemDelegate; + bool m_hasUniformAnnotationItemSizes = false; + bool m_isDefaultAnnotationItemDelegate = true; + mutable QPixmap m_arrow; mutable QColor m_oldBackgroundColor; @@ -348,6 +375,7 @@ private Q_SLOTS: void showBlock(); + void handleDestroyedAnnotationItemDelegate(); private: QString m_hoveredAnnotationGroupIdentifier; diff --git a/src/view/kateviewhelpers.cpp b/src/view/kateviewhelpers.cpp --- a/src/view/kateviewhelpers.cpp +++ b/src/view/kateviewhelpers.cpp @@ -6,6 +6,7 @@ Copyright (C) 2001 Christoph Cullmann Copyright (C) 2011 Svyatoslav Kuzmich Copyright (C) 2012 Kåre Särs (Minimap) + Copyright 2017-2018 Friedrich W. H. Kossebau This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -32,6 +33,7 @@ #include "katedocument.h" #include #include "katerenderer.h" +#include "kateannotationitemdelegate.h" #include "kateview.h" #include "kateviewinternal.h" #include "katelayoutcache.h" @@ -1436,6 +1438,7 @@ , m_maxCharWidth(0.0) , iconPaneWidth(16) , m_annotationBorderWidth(6) + , m_annotationItemDelegate(new KateAnnotationItemDelegate(m_viewInternal, this)) , m_foldingPreview(nullptr) , m_foldingRange(nullptr) , m_nextHighlightBlock(-2) @@ -1447,6 +1450,9 @@ m_doc->setMarkDescription(MarkInterface::markType01, i18n("Bookmark")); m_doc->setMarkPixmap(MarkInterface::markType01, QIcon::fromTheme(QStringLiteral("bookmarks")).pixmap(32, 32)); + connect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, + this, &KateIconBorder::updateAnnotationBorderWidth); + updateFont(); m_delayFoldingHlTimer.setSingleShot(true); @@ -1484,6 +1490,12 @@ m_annotationBorderOn = enable; + // make sure the tooltip is hidden + if (!m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { + m_hoveredAnnotationGroupIdentifier.clear(); + hideAnnotationTooltip(); + } + emit m_view->annotationBorderVisibilityChanged(m_view, enable); updateGeometry(); @@ -1496,7 +1508,6 @@ // remove hovering if it's still there if (m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { m_hoveredAnnotationGroupIdentifier.clear(); - hideAnnotationTooltip(); QTimer::singleShot(0, this, SLOT(update())); } } @@ -1615,6 +1626,8 @@ // the icon pane scales with the font... iconPaneWidth = fm.height(); + calcAnnotationBorderWidth(); + updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); @@ -1708,6 +1721,215 @@ painter.setRenderHint(QPainter::Antialiasing, false); } +/** + * Helper class for an identifier which can be an empty or non-empty string or invalid. + * Avoids complicated explicit statements in code dealing with the identifier + * received as QVariant from a model. + */ +class KateAnnotationGroupIdentifier +{ +public: + KateAnnotationGroupIdentifier(const QVariant &identifier) + : m_isValid(identifier.isValid() && identifier.canConvert()) + , m_id(m_isValid ? identifier.toString() : QString()) + { + } + KateAnnotationGroupIdentifier() = default; + KateAnnotationGroupIdentifier(const KateAnnotationGroupIdentifier &rhs) = default; + + KateAnnotationGroupIdentifier& operator=(const KateAnnotationGroupIdentifier &rhs) + { + m_isValid = rhs.m_isValid; + m_id = rhs.m_id; + return *this; + } + KateAnnotationGroupIdentifier& operator=(const QVariant &identifier) + { + m_isValid = (identifier.isValid() && identifier.canConvert()); + if (m_isValid) { + m_id = identifier.toString(); + } else { + m_id.clear(); + } + return *this; + } + + bool operator==(const KateAnnotationGroupIdentifier &rhs) const + { + return (m_isValid == rhs.m_isValid) && (!m_isValid || (m_id == rhs.m_id)); + } + bool operator!=(const KateAnnotationGroupIdentifier &rhs) const + { + return (m_isValid != rhs.m_isValid) || (m_isValid && (m_id != rhs.m_id)); + } + + void clear() + { + m_isValid = false; + m_id.clear(); + } + bool isValid() const { return m_isValid; } + const QString& id() const { return m_id; } + +private: + bool m_isValid = false; + QString m_id; +}; + +/** + * Helper class for iterative calculation of data regarding the position + * of a line with regard to annotation item grouping. + */ +class KateAnnotationGroupPositionState +{ +public: + /** + * @param startz first rendered displayed line + * @param isUsed flag whether the KateAnnotationGroupPositionState object will + * be used or is just created due to being on the stack + */ + KateAnnotationGroupPositionState(KateViewInternal *viewInternal, + const KTextEditor::AnnotationModel *model, + const QString &hoveredAnnotationGroupIdentifier, + uint startz, + bool isUsed); + /** + * @param styleOption option to fill with data for the given line + * @param z rendered displayed line + * @param realLine real line which is rendered here (passed to avoid another look-up) + */ + void nextLine(KTextEditor::StyleOptionAnnotationItem &styleOption, uint z, int realLine); + +private: + KateViewInternal *m_viewInternal; + const KTextEditor::AnnotationModel * const m_model; + const QString m_hoveredAnnotationGroupIdentifier; + + int m_visibleWrappedLineInAnnotationGroup = -1; + KateAnnotationGroupIdentifier m_lastAnnotationGroupIdentifier; + KateAnnotationGroupIdentifier m_nextAnnotationGroupIdentifier; + bool m_isSameAnnotationGroupsSinceLast = false; +}; + +KateAnnotationGroupPositionState::KateAnnotationGroupPositionState(KateViewInternal *viewInternal, + const KTextEditor::AnnotationModel *model, + const QString &hoveredAnnotationGroupIdentifier, + uint startz, + bool isUsed) + : m_viewInternal(viewInternal) + , m_model(model) + , m_hoveredAnnotationGroupIdentifier(hoveredAnnotationGroupIdentifier) +{ + if (!isUsed) { + return; + } + + if (!m_model || (static_cast(startz) >= m_viewInternal->cache()->viewCacheLineCount())) { + return; + } + + const auto realLineAtStart = m_viewInternal->cache()->viewLine(startz).line(); + m_nextAnnotationGroupIdentifier = m_model->data(realLineAtStart, + (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole); + if (m_nextAnnotationGroupIdentifier.isValid()) { + // estimate state of annotation group before first rendered line + if (startz == 0) { + if (realLineAtStart > 0) { + // TODO: here we would want to scan until the next line that would be displayed, + // to see if there are any group changes until then + // for now simply taking neighbour line into account, not a grave bug on the first displayed line + m_lastAnnotationGroupIdentifier = m_model->data(realLineAtStart - 1, + (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole); + m_isSameAnnotationGroupsSinceLast = (m_lastAnnotationGroupIdentifier == m_nextAnnotationGroupIdentifier); + } + } else { + const auto realLineBeforeStart = m_viewInternal->cache()->viewLine(startz-1).line(); + m_lastAnnotationGroupIdentifier = m_model->data(realLineBeforeStart, (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole); + if (m_lastAnnotationGroupIdentifier.isValid()) { + if (m_lastAnnotationGroupIdentifier.id() == m_nextAnnotationGroupIdentifier.id()) { + m_isSameAnnotationGroupsSinceLast = true; + // estimate m_visibleWrappedLineInAnnotationGroup from lines before startz + for (uint z = startz; z > 0; --z) { + const auto realLine = m_viewInternal->cache()->viewLine(z-1).line(); + const KateAnnotationGroupIdentifier identifier = m_model->data(realLine, (Qt::ItemDataRole)KTextEditor::AnnotationModel::GroupIdentifierRole); + if (identifier != m_lastAnnotationGroupIdentifier) { + break; + } + ++m_visibleWrappedLineInAnnotationGroup; + } + } + } + } + } +} + +void KateAnnotationGroupPositionState::nextLine(KTextEditor::StyleOptionAnnotationItem &styleOption, + uint z, int realLine) +{ + styleOption.wrappedLine = m_viewInternal->cache()->viewLine(z).viewLine(); + styleOption.wrappedLineCount = m_viewInternal->cache()->viewLineCount(realLine); + + // Estimate position in group + const KateAnnotationGroupIdentifier annotationGroupIdentifier = m_nextAnnotationGroupIdentifier; + bool isSameAnnotationGroupsSinceThis = false; + // Calculate next line's group identifier + // shortcut: assuming wrapped lines are always displayed together, test is simple + if (styleOption.wrappedLine+1 < styleOption.wrappedLineCount) { + m_nextAnnotationGroupIdentifier = annotationGroupIdentifier; + isSameAnnotationGroupsSinceThis = true; + } else { + if (static_cast(z+1) < m_viewInternal->cache()->viewCacheLineCount()) { + const int realLineAfter = m_viewInternal->cache()->viewLine(z+1).line(); + // search for any realLine with a different group id, also the non-displayed + int rl = realLine + 1; + for (; rl <= realLineAfter; ++rl) { + m_nextAnnotationGroupIdentifier = m_model->data(rl, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole); + if (!m_nextAnnotationGroupIdentifier.isValid() || + (m_nextAnnotationGroupIdentifier.id() != annotationGroupIdentifier.id())) { + break; + } + } + isSameAnnotationGroupsSinceThis = (rl > realLineAfter); + if (rl < realLineAfter) { + m_nextAnnotationGroupIdentifier = m_model->data(realLineAfter, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole); + } + } else { + // TODO: check next line after display end + m_nextAnnotationGroupIdentifier.clear(); + isSameAnnotationGroupsSinceThis = false; + } + } + + if (annotationGroupIdentifier.isValid()) { + if (m_hoveredAnnotationGroupIdentifier == annotationGroupIdentifier.id()) { + styleOption.state |= QStyle::State_MouseOver; + } else { + styleOption.state &= ~QStyle::State_MouseOver; + } + + if (m_isSameAnnotationGroupsSinceLast) { + ++m_visibleWrappedLineInAnnotationGroup; + } else { + m_visibleWrappedLineInAnnotationGroup = 0; + } + + styleOption.annotationItemGroupingPosition = StyleOptionAnnotationItem::InGroup; + if (!m_isSameAnnotationGroupsSinceLast) { + styleOption.annotationItemGroupingPosition |= StyleOptionAnnotationItem::GroupBegin; + } + if (!isSameAnnotationGroupsSinceThis) { + styleOption.annotationItemGroupingPosition |= StyleOptionAnnotationItem::GroupEnd; + } + } else { + m_visibleWrappedLineInAnnotationGroup = 0; + } + styleOption.visibleWrappedLineInGroup = m_visibleWrappedLineInAnnotationGroup; + + m_lastAnnotationGroupIdentifier = m_nextAnnotationGroupIdentifier; + m_isSameAnnotationGroupsSinceLast = isSameAnnotationGroupsSinceThis; +} + + void KateIconBorder::paintBorder(int /*x*/, int y, int /*width*/, int height) { uint h = m_view->renderer()->lineHeight(); @@ -1746,6 +1968,9 @@ KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); + KateAnnotationGroupPositionState annotationGroupPositionState(m_viewInternal, model, + m_hoveredAnnotationGroupIdentifier, + startz, m_annotationBorderOn); for (uint z = startz; z <= endz; z++) { int y = h * z; @@ -1807,60 +2032,20 @@ p.setPen(m_view->renderer()->config()->lineNumberColor()); p.setBrush(m_view->renderer()->config()->lineNumberColor()); - int borderWidth = m_annotationBorderWidth; - p.drawLine(lnX + borderWidth + 1, y, lnX + borderWidth + 1, y + h); + const qreal borderX = lnX + m_annotationBorderWidth + 0.5; + p.drawLine(QPointF(borderX, y+0.5), QPointF(borderX, y + h - 0.5)); if ((realLine > -1) && model) { - // Fetch data from the model - QVariant text = model->data(realLine, Qt::DisplayRole); - QVariant foreground = model->data(realLine, Qt::ForegroundRole); - QVariant background = model->data(realLine, Qt::BackgroundRole); - // Fill the background - if (background.isValid()) { - p.fillRect(lnX, y, borderWidth + 1, h, background.value()); - } - // Set the pen for drawing the foreground - if (foreground.isValid()) { - p.setPen(foreground.value()); - } - - // Draw a border around all adjacent entries that have the same text as the currently hovered one - const QVariant identifier = model->data( realLine, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ); - if( m_hoveredAnnotationGroupIdentifier == identifier.toString() ) { - p.drawLine(lnX, y, lnX, y + h); - p.drawLine(lnX + borderWidth, y, lnX + borderWidth, y + h); - - QVariant beforeText = model->data(realLine - 1, Qt::DisplayRole); - QVariant afterText = model->data(realLine + 1, Qt::DisplayRole); - if (((beforeText.isValid() && beforeText.canConvert() - && text.isValid() && text.canConvert() - && beforeText.toString() != text.toString()) || realLine == 0) - && m_viewInternal->cache()->viewLine(z).viewLine() == 0) { - p.drawLine(lnX + 1, y, lnX + borderWidth, y); - } - - if (((afterText.isValid() && afterText.canConvert() - && text.isValid() && text.canConvert() - && afterText.toString() != text.toString()) - || realLine == m_view->doc()->lines() - 1) - && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine) - 1) { - p.drawLine(lnX + 1, y + h - 1, lnX + borderWidth, y + h - 1); - } - } - if (foreground.isValid()) { - QPen pen = p.pen(); - pen.setWidth(1); - p.setPen(pen); - } + KTextEditor::StyleOptionAnnotationItem styleOption; + initStyleOption(&styleOption); + styleOption.rect.setRect(lnX, y, m_annotationBorderWidth, h); + annotationGroupPositionState.nextLine(styleOption, z, realLine); - // Now draw the normal text - if (text.isValid() && text.canConvert() && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { - p.drawText(lnX + 3, y, borderWidth - 3, h, Qt::AlignLeft | Qt::AlignVCenter, text.toString()); - } + m_annotationItemDelegate->paint(&p, styleOption, model, realLine); } - // adjust current X position and reset the pen and brush - lnX += borderWidth + 2; + // adjust current X position + lnX += m_annotationBorderWidth + /* separator line width */1; } // line number @@ -2201,16 +2386,22 @@ if (model) { m_hoveredAnnotationGroupIdentifier = model->data( t.line(), (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); - showAnnotationTooltip(t.line(), e->globalPos()); + const QPoint viewRelativePos = m_view->mapFromGlobal(e->globalPos()); + QHelpEvent helpEvent(QEvent::ToolTip, viewRelativePos, e->globalPos()); + KTextEditor::StyleOptionAnnotationItem styleOption; + initStyleOption(&styleOption); + styleOption.rect = annotationLineRectInView(t.line()); + setStyleOptionLineData(&styleOption, e->y(), t.line(), model, m_hoveredAnnotationGroupIdentifier); + m_annotationItemDelegate->helpEvent(&helpEvent, m_view, styleOption, model, t.line()); + QTimer::singleShot(0, this, SLOT(update())); } } else { if (positionToArea(e->pos()) == IconBorder) { m_doc->requestMarkTooltip(t.line(), e->globalPos()); } m_hoveredAnnotationGroupIdentifier.clear(); - hideAnnotationTooltip(); QTimer::singleShot(0, this, SLOT(update())); } if (positionToArea(e->pos()) != IconBorder) { @@ -2393,36 +2584,119 @@ } } -void KateIconBorder::showAnnotationTooltip(int line, const QPoint &pos) +KTextEditor::AbstractAnnotationItemDelegate* KateIconBorder::annotationItemDelegate() const { - KTextEditor::AnnotationModel *model = m_view->annotationModel() ? - m_view->annotationModel() : m_doc->annotationModel(); + return m_annotationItemDelegate; +} - if (model) { - QVariant data = model->data(line, Qt::ToolTipRole); - QString tip = data.toString(); - if (!tip.isEmpty()) { - QToolTip::showText(pos, data.toString(), this); +void KateIconBorder::setAnnotationItemDelegate(KTextEditor::AbstractAnnotationItemDelegate *delegate) +{ + if (delegate == m_annotationItemDelegate) { + return; + } + + // reset to default, but already on that? + if (!delegate && m_isDefaultAnnotationItemDelegate) { + // nothing to do + return; + } + + // make sure the tooltip is hidden + if (m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { + m_hoveredAnnotationGroupIdentifier.clear(); + hideAnnotationTooltip(); + } + + disconnect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, + this, &KateIconBorder::updateAnnotationBorderWidth); + if (!m_isDefaultAnnotationItemDelegate) { + disconnect(m_annotationItemDelegate, &QObject::destroyed, + this, &KateIconBorder::handleDestroyedAnnotationItemDelegate); + } + + if (!delegate) { + // reset to a default delegate + m_annotationItemDelegate = new KateAnnotationItemDelegate(m_viewInternal, this); + m_isDefaultAnnotationItemDelegate = true; + } else { + // drop any default delegate + if (m_isDefaultAnnotationItemDelegate) { + delete m_annotationItemDelegate; + m_isDefaultAnnotationItemDelegate = false; } + + m_annotationItemDelegate = delegate; + // catch delegate being destroyed + connect(delegate, &QObject::destroyed, + this, &KateIconBorder::handleDestroyedAnnotationItemDelegate); + } + + connect(m_annotationItemDelegate, &AbstractAnnotationItemDelegate::sizeHintChanged, + this, &KateIconBorder::updateAnnotationBorderWidth); + + if (m_annotationBorderOn) { + updateGeometry(); + + QTimer::singleShot(0, this, SLOT(update())); } } -int KateIconBorder::annotationLineWidth(int line) +void KateIconBorder::handleDestroyedAnnotationItemDelegate() { - KTextEditor::AnnotationModel *model = m_view->annotationModel() ? - m_view->annotationModel() : m_doc->annotationModel(); + setAnnotationItemDelegate(nullptr); +} - if (model) { - QVariant data = model->data(line, Qt::DisplayRole); - return data.toString().length() * m_maxCharWidth + 8; +void KateIconBorder::initStyleOption(KTextEditor::StyleOptionAnnotationItem* styleOption) const +{ + styleOption->initFrom(this); + styleOption->view = m_view; + styleOption->decorationSize = QSize(iconPaneWidth, iconPaneWidth); + styleOption->contentFontMetrics = m_view->renderer()->config()->fontMetrics(); +} + +void KateIconBorder::setStyleOptionLineData(KTextEditor::StyleOptionAnnotationItem* styleOption, + int y, + int realLine, + const KTextEditor::AnnotationModel *model, + const QString &annotationGroupIdentifier) const +{ + // calculate rendered displayed line + const uint h = m_view->renderer()->lineHeight(); + const uint z = (y / h); + + KateAnnotationGroupPositionState annotationGroupPositionState(m_viewInternal, model, + annotationGroupIdentifier, + z, true); + annotationGroupPositionState.nextLine(*styleOption, z, realLine); +} + + +QRect KateIconBorder::annotationLineRectInView(int line) const +{ + int x = 0; + if (m_iconBorderOn) { + x += iconPaneWidth + 2; } - return 8; + const int y = m_view->m_viewInternal->lineToY(line); + + return QRect(x, y, m_annotationBorderWidth, m_view->renderer()->lineHeight()); } void KateIconBorder::updateAnnotationLine(int line) { - if (annotationLineWidth(line) > m_annotationBorderWidth) { - m_annotationBorderWidth = annotationLineWidth(line); + // TODO: why has the default value been 8, where is that magic number from? + int width = 8; + KTextEditor::AnnotationModel *model = m_view->annotationModel() ? + m_view->annotationModel() : m_doc->annotationModel(); + + if (model) { + KTextEditor::StyleOptionAnnotationItem styleOption; + initStyleOption(&styleOption); + width = m_annotationItemDelegate->sizeHint(styleOption, model, line).width(); + } + + if (width > m_annotationBorderWidth) { + m_annotationBorderWidth = width; updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); @@ -2443,27 +2717,40 @@ void KateIconBorder::hideAnnotationTooltip() { - QToolTip::hideText(); + m_annotationItemDelegate->hideTooltip(m_view); } void KateIconBorder::updateAnnotationBorderWidth() { + calcAnnotationBorderWidth(); + + updateGeometry(); + + QTimer::singleShot(0, this, SLOT(update())); +} + +void KateIconBorder::calcAnnotationBorderWidth() +{ + // TODO: another magic number, not matching the one in updateAnnotationLine() m_annotationBorderWidth = 6; KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if (model) { - for (int i = 0; i < m_view->doc()->lines(); i++) { - int curwidth = annotationLineWidth(i); - if (curwidth > m_annotationBorderWidth) { - m_annotationBorderWidth = curwidth; + KTextEditor::StyleOptionAnnotationItem styleOption; + initStyleOption(&styleOption); + + const int lineCount = m_view->doc()->lines(); + if (lineCount > 0) { + const int checkedLineCount = m_hasUniformAnnotationItemSizes ? 1 : lineCount; + for (int i = 0; i < checkedLineCount; ++i) { + const int curwidth = m_annotationItemDelegate->sizeHint(styleOption, model, i).width(); + if (curwidth > m_annotationBorderWidth) { + m_annotationBorderWidth = curwidth; + } } } } - - updateGeometry(); - - QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::annotationModelChanged(KTextEditor::AnnotationModel *oldmodel, KTextEditor::AnnotationModel *newmodel) diff --git a/src/view/kateviewinternal.h b/src/view/kateviewinternal.h --- a/src/view/kateviewinternal.h +++ b/src/view/kateviewinternal.h @@ -52,6 +52,8 @@ class KateIconBorder; class KateScrollBar; +class KateAnnotationItemDelegate; +class KateAnnotationGroupPositionState; class KateTextLayout; class KateTextAnimation; class KateAbstractInputMode; @@ -66,6 +68,8 @@ friend class KTextEditor::ViewPrivate; friend class KateIconBorder; friend class KateScrollBar; + friend class KateAnnotationItemDelegate; + friend class KateAnnotationGroupPositionState; friend class CalculatingCursor; friend class BoundedCursor; friend class WrappingCursor;