Refactor HighlightScrollBar

Rename it into HighlightScrollBarController.
Don't derive it anymore from QScrollBar.
Make it based on QObject and decorate
the existing instance of QAbstractScrollArea as needed.

Fix the highlight of the shared scrollbar of the SideBySideDiffEditor.
Both left and right diff editors have their own
HighlightScrollBarController and their own separate overlays, but both
overlays are created as children of the same right editor instance.

Synchronize also the cursor between left and right editors.
Make highlight current line working.

Make the overlay transparent for mouse events - this fixes
issues on macOS when scolling over invisible scrollbar.

Change-Id: Iab05c360173e09d8748658c59785da86438a7189
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Jarek Kobus
2017-11-02 13:06:38 +01:00
parent 11d002968e
commit 990da15c1e
10 changed files with 319 additions and 252 deletions

View File

@@ -223,8 +223,8 @@ Project {
"findtoolwindow.cpp", "findtoolwindow.cpp",
"findtoolwindow.h", "findtoolwindow.h",
"findwidget.ui", "findwidget.ui",
"highlightscrollbar.cpp", "highlightscrollbarcontroller.cpp",
"highlightscrollbar.h", "highlightscrollbarcontroller.h",
"ifindfilter.cpp", "ifindfilter.cpp",
"ifindfilter.h", "ifindfilter.h",
"ifindsupport.cpp", "ifindsupport.cpp",

View File

@@ -4,6 +4,7 @@ HEADERS += \
$$PWD/findplugin.h \ $$PWD/findplugin.h \
$$PWD/findtoolbar.h \ $$PWD/findtoolbar.h \
$$PWD/findtoolwindow.h \ $$PWD/findtoolwindow.h \
$$PWD/highlightscrollbarcontroller.h \
$$PWD/ifindfilter.h \ $$PWD/ifindfilter.h \
$$PWD/ifindsupport.h \ $$PWD/ifindsupport.h \
$$PWD/itemviewfind.h \ $$PWD/itemviewfind.h \
@@ -16,7 +17,6 @@ HEADERS += \
$$PWD/searchresultwidget.h \ $$PWD/searchresultwidget.h \
$$PWD/searchresultwindow.h \ $$PWD/searchresultwindow.h \
$$PWD/textfindconstants.h \ $$PWD/textfindconstants.h \
$$PWD/highlightscrollbar.h \
$$PWD/searchresultitem.h $$PWD/searchresultitem.h
SOURCES += \ SOURCES += \
@@ -25,6 +25,7 @@ SOURCES += \
$$PWD/findplugin.cpp \ $$PWD/findplugin.cpp \
$$PWD/findtoolbar.cpp \ $$PWD/findtoolbar.cpp \
$$PWD/findtoolwindow.cpp \ $$PWD/findtoolwindow.cpp \
$$PWD/highlightscrollbarcontroller.cpp \
$$PWD/ifindfilter.cpp \ $$PWD/ifindfilter.cpp \
$$PWD/ifindsupport.cpp \ $$PWD/ifindsupport.cpp \
$$PWD/itemviewfind.cpp \ $$PWD/itemviewfind.cpp \
@@ -33,8 +34,7 @@ SOURCES += \
$$PWD/searchresulttreemodel.cpp \ $$PWD/searchresulttreemodel.cpp \
$$PWD/searchresulttreeview.cpp \ $$PWD/searchresulttreeview.cpp \
$$PWD/searchresultwidget.cpp \ $$PWD/searchresultwidget.cpp \
$$PWD/searchresultwindow.cpp \ $$PWD/searchresultwindow.cpp
$$PWD/highlightscrollbar.cpp
FORMS += \ FORMS += \

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -23,13 +23,14 @@
** **
****************************************************************************/ ****************************************************************************/
#include "highlightscrollbar.h" #include "highlightscrollbarcontroller.h"
#include <utils/asconst.h> #include <utils/asconst.h>
#include <utils/qtcassert.h>
#include <QAbstractScrollArea>
#include <QPainter> #include <QPainter>
#include <QResizeEvent> #include <QResizeEvent>
#include <QScrollBar>
#include <QStyle> #include <QStyle>
#include <QStyleOptionSlider> #include <QStyleOptionSlider>
#include <QTimer> #include <QTimer>
@@ -40,164 +41,45 @@ namespace Core {
class HighlightScrollBarOverlay : public QWidget class HighlightScrollBarOverlay : public QWidget
{ {
public: public:
HighlightScrollBarOverlay(HighlightScrollBar *scrollBar) HighlightScrollBarOverlay(HighlightScrollBarController *scrollBarController)
: QWidget(scrollBar) : QWidget(scrollBarController->scrollArea())
, m_visibleRange(0.0) , m_scrollBar(scrollBarController->scrollBar())
, m_offset(0.0) , m_highlightController(scrollBarController)
, m_cacheUpdateScheduled(false) {
, m_scrollBar(scrollBar) setAttribute(Qt::WA_TransparentForMouseEvents);
{} m_scrollBar->parentWidget()->installEventFilter(this);
doResize();
doMove();
show();
}
void doResize()
{
resize(m_scrollBar->size());
}
void doMove()
{
move(parentWidget()->mapFromGlobal(m_scrollBar->mapToGlobal(m_scrollBar->pos())));
}
void scheduleUpdate(); void scheduleUpdate();
void updateCache();
void adjustPosition();
float m_visibleRange;
float m_offset;
QHash<Id, QVector<Highlight> > m_highlights;
bool m_cacheUpdateScheduled;
QMap<int, Highlight> m_cache;
protected: protected:
void paintEvent(QPaintEvent *paintEvent) override; void paintEvent(QPaintEvent *paintEvent) override;
bool eventFilter(QObject *object, QEvent *event) override;
private: private:
HighlightScrollBar *m_scrollBar; void updateCache();
QRect overlayRect() const;
bool m_cacheUpdateScheduled = true;
QMap<int, Highlight> m_cache;
QScrollBar *m_scrollBar;
HighlightScrollBarController *m_highlightController;
}; };
HighlightScrollBar::HighlightScrollBar(Qt::Orientation orientation, QWidget *parent)
: QScrollBar(orientation, parent)
, m_widget(parent)
, m_overlay(new HighlightScrollBarOverlay(this))
{
connect(m_overlay, &HighlightScrollBarOverlay::destroyed,
this, &HighlightScrollBar::overlayDestroyed);
// valueChanged(0) flashes transient scroll bars, which is needed
// for a correct initialization.
emit valueChanged(0);
}
HighlightScrollBar::~HighlightScrollBar()
{
if (!m_overlay || m_overlay->parent() == this)
return;
delete m_overlay;
}
void HighlightScrollBar::setVisibleRange(float visibleRange)
{
if (!m_overlay)
return;
m_overlay->m_visibleRange = visibleRange;
}
void HighlightScrollBar::setRangeOffset(float offset)
{
if (!m_overlay)
return;
m_overlay->m_offset = offset;
}
QRect HighlightScrollBar::overlayRect()
{
QStyleOptionSlider opt;
initStyleOption(&opt);
return style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this);
}
void HighlightScrollBar::overlayDestroyed()
{
m_overlay = 0;
}
void HighlightScrollBar::addHighlight(Highlight highlight)
{
if (!m_overlay)
return;
m_overlay->m_highlights[highlight.category] << highlight;
m_overlay->scheduleUpdate();
}
void HighlightScrollBar::removeHighlights(Id category)
{
if (!m_overlay)
return;
m_overlay->m_highlights.remove(category);
m_overlay->scheduleUpdate();
}
void HighlightScrollBar::removeAllHighlights()
{
if (!m_overlay)
return;
m_overlay->m_highlights.clear();
m_overlay->scheduleUpdate();
}
bool HighlightScrollBar::eventFilter(QObject *obj, QEvent *event)
{
if (obj == m_widget && m_overlay && m_widget == m_overlay->parent() &&
(event->type() == QEvent::Resize || event->type() == QEvent::Move)) {
QStyleOptionSlider opt;
initStyleOption(&opt);
const int width = style()->pixelMetric(QStyle::PM_ScrollBarExtent, &opt, this);
m_overlay->move(m_widget->width() - width, 0);
resize(width, m_widget->height());
}
return false;
}
void HighlightScrollBar::resizeEvent(QResizeEvent *event)
{
if (!m_overlay)
return;
QScrollBar::resizeEvent(event);
m_overlay->resize(size());
}
void HighlightScrollBar::moveEvent(QMoveEvent *event)
{
if (!m_overlay)
return;
QScrollBar::moveEvent(event);
m_overlay->adjustPosition();
}
void HighlightScrollBar::showEvent(QShowEvent *event)
{
if (!m_overlay)
return;
QScrollBar::showEvent(event);
if (parentWidget() != this) {
m_widget->removeEventFilter(this);
m_overlay->setParent(this);
m_overlay->adjustPosition();
m_overlay->show();
}
}
void HighlightScrollBar::hideEvent(QHideEvent *event)
{
if (!m_overlay)
return;
QScrollBar::hideEvent(event);
if (parentWidget() != m_widget) {
m_widget->installEventFilter(this);
m_overlay->setParent(m_widget);
m_overlay->adjustPosition();
m_overlay->show();
}
}
void HighlightScrollBar::changeEvent(QEvent *event)
{
// Workaround for QTBUG-45579
if (event->type() == QEvent::ParentChange)
setStyle(style());
}
void HighlightScrollBarOverlay::scheduleUpdate() void HighlightScrollBarOverlay::scheduleUpdate()
{ {
if (m_cacheUpdateScheduled) if (m_cacheUpdateScheduled)
@@ -207,28 +89,6 @@ void HighlightScrollBarOverlay::scheduleUpdate()
QTimer::singleShot(0, this, static_cast<void (QWidget::*)()>(&QWidget::update)); QTimer::singleShot(0, this, static_cast<void (QWidget::*)()>(&QWidget::update));
} }
void HighlightScrollBarOverlay::updateCache()
{
if (!m_cacheUpdateScheduled)
return;
m_cache.clear();
const QList<Id> &categories = m_highlights.keys();
for (const Id &category : categories) {
for (const Highlight &highlight : Utils::asConst(m_highlights[category])) {
Highlight oldHighlight = m_cache[highlight.position];
if (highlight.priority > oldHighlight.priority)
m_cache[highlight.position] = highlight;
}
}
m_cacheUpdateScheduled = false;
}
void HighlightScrollBarOverlay::adjustPosition()
{
move(parentWidget()->mapFromGlobal(m_scrollBar->mapToGlobal(m_scrollBar->pos())));
}
void HighlightScrollBarOverlay::paintEvent(QPaintEvent *paintEvent) void HighlightScrollBarOverlay::paintEvent(QPaintEvent *paintEvent)
{ {
QWidget::paintEvent(paintEvent); QWidget::paintEvent(paintEvent);
@@ -238,23 +98,23 @@ void HighlightScrollBarOverlay::paintEvent(QPaintEvent *paintEvent)
if (m_cache.isEmpty()) if (m_cache.isEmpty())
return; return;
const QRect &rect = m_scrollBar->overlayRect(); const QRect &rect = overlayRect();
Utils::Theme::Color previousColor = Utils::Theme::TextColorNormal; Utils::Theme::Color previousColor = Utils::Theme::TextColorNormal;
Highlight::Priority previousPriority = Highlight::LowPriority; Highlight::Priority previousPriority = Highlight::LowPriority;
QRect *previousRect = 0; QRect *previousRect = nullptr;
const int scrollbarRange = m_scrollBar->maximum() + m_scrollBar->pageStep(); const int scrollbarRange = m_scrollBar->maximum() + m_scrollBar->pageStep();
const int range = qMax(m_visibleRange, float(scrollbarRange)); const int range = qMax(m_highlightController->visibleRange(), float(scrollbarRange));
const int horizontalMargin = 3; const int horizontalMargin = 3;
const int resultWidth = rect.width() - 2 * horizontalMargin + 1; const int resultWidth = rect.width() - 2 * horizontalMargin + 1;
const int resultHeight = qMin(int(rect.height() / range) + 1, 4); const int resultHeight = qMin(int(rect.height() / range) + 1, 4);
const int offset = rect.height() / range * m_offset; const int offset = rect.height() / range * m_highlightController->rangeOffset();
const int verticalMargin = ((rect.height() / range) - resultHeight) / 2; const int verticalMargin = ((rect.height() / range) - resultHeight) / 2;
int previousBottom = -1; int previousBottom = -1;
QHash<Utils::Theme::Color, QVector<QRect> > highlights; QHash<Utils::Theme::Color, QVector<QRect> > highlights;
for (Highlight currentHighlight : Utils::asConst(m_cache)) { for (const Highlight &currentHighlight : Utils::asConst(m_cache)) {
// Calculate top and bottom // Calculate top and bottom
int top = rect.top() + offset + verticalMargin int top = rect.top() + offset + verticalMargin
+ float(currentHighlight.position) / range * rect.height(); + float(currentHighlight.position) / range * rect.height();
@@ -301,6 +161,50 @@ void HighlightScrollBarOverlay::paintEvent(QPaintEvent *paintEvent)
} }
} }
bool HighlightScrollBarOverlay::eventFilter(QObject *object, QEvent *event)
{
switch (event->type()) {
case QEvent::Move:
doMove();
break;
case QEvent::Resize:
doResize();
break;
case QEvent::ZOrderChange:
raise();
break;
default:
break;
}
return QWidget::eventFilter(object, event);
}
void HighlightScrollBarOverlay::updateCache()
{
if (!m_cacheUpdateScheduled)
return;
m_cache.clear();
const QHash<Id, QVector<Highlight>> highlights = m_highlightController->highlights();
const QList<Id> &categories = highlights.keys();
for (const Id &category : categories) {
for (const Highlight &highlight : highlights.value(category)) {
const Highlight oldHighlight = m_cache.value(highlight.position);
if (highlight.priority > oldHighlight.priority)
m_cache[highlight.position] = highlight;
}
}
m_cacheUpdateScheduled = false;
}
QRect HighlightScrollBarOverlay::overlayRect() const
{
QStyleOptionSlider opt = qt_qscrollbarStyleOption(m_scrollBar);
return m_scrollBar->style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, m_scrollBar);
}
/////////////
Highlight::Highlight(Id category_, int position_, Highlight::Highlight(Id category_, int position_,
Theme::Color color_, Highlight::Priority priority_) Theme::Color color_, Highlight::Priority priority_)
: category(category_) : category(category_)
@@ -310,4 +214,95 @@ Highlight::Highlight(Id category_, int position_,
{ {
} }
/////////////
HighlightScrollBarController::~HighlightScrollBarController()
{
if (m_overlay)
delete m_overlay;
}
QScrollBar *HighlightScrollBarController::scrollBar() const
{
if (m_scrollArea)
return m_scrollArea->verticalScrollBar();
return nullptr;
}
QAbstractScrollArea *HighlightScrollBarController::scrollArea() const
{
return m_scrollArea;
}
void HighlightScrollBarController::setScrollArea(QAbstractScrollArea *scrollArea)
{
if (m_scrollArea == scrollArea)
return;
if (m_overlay) {
delete m_overlay;
m_overlay = nullptr;
}
m_scrollArea = scrollArea;
if (m_scrollArea) {
m_overlay = new HighlightScrollBarOverlay(this);
m_overlay->scheduleUpdate();
}
}
float HighlightScrollBarController::visibleRange() const
{
return m_visibleRange;
}
void HighlightScrollBarController::setVisibleRange(float visibleRange)
{
m_visibleRange = visibleRange;
}
float HighlightScrollBarController::rangeOffset() const
{
return m_rangeOffset;
}
void HighlightScrollBarController::setRangeOffset(float offset)
{
m_rangeOffset = offset;
}
QHash<Id, QVector<Highlight>> HighlightScrollBarController::highlights() const
{
return m_highlights;
}
void HighlightScrollBarController::addHighlight(Highlight highlight)
{
if (!m_overlay)
return;
m_highlights[highlight.category] << highlight;
m_overlay->scheduleUpdate();
}
void HighlightScrollBarController::removeHighlights(Id category)
{
if (!m_overlay)
return;
m_highlights.remove(category);
m_overlay->scheduleUpdate();
}
void HighlightScrollBarController::removeAllHighlights()
{
if (!m_overlay)
return;
m_highlights.clear();
m_overlay->scheduleUpdate();
}
} // namespace Core } // namespace Core

View File

@@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
** This file is part of Qt Creator. ** This file is part of Qt Creator.
@@ -25,14 +25,19 @@
#pragma once #pragma once
#include <QMap> #include <QHash>
#include <QScrollBar> #include <QPointer>
#include <QSet> #include <QVector>
#include <coreplugin/core_global.h> #include <coreplugin/core_global.h>
#include <coreplugin/id.h> #include <coreplugin/id.h>
#include <utils/theme/theme.h> #include <utils/theme/theme.h>
QT_BEGIN_NAMESPACE
class QAbstractScrollArea;
class QScrollBar;
QT_END_NAMESPACE
namespace Core { namespace Core {
struct CORE_EXPORT Highlight struct CORE_EXPORT Highlight
@@ -56,38 +61,34 @@ struct CORE_EXPORT Highlight
class HighlightScrollBarOverlay; class HighlightScrollBarOverlay;
class CORE_EXPORT HighlightScrollBar : public QScrollBar class CORE_EXPORT HighlightScrollBarController
{ {
Q_OBJECT
public: public:
HighlightScrollBar(Qt::Orientation orientation, QWidget *parent = 0); HighlightScrollBarController() = default;
~HighlightScrollBar() override; ~HighlightScrollBarController();
QScrollBar *scrollBar() const;
QAbstractScrollArea *scrollArea() const;
void setScrollArea(QAbstractScrollArea *scrollArea);
float visibleRange() const;
void setVisibleRange(float visibleRange); void setVisibleRange(float visibleRange);
float rangeOffset() const;
void setRangeOffset(float offset); void setRangeOffset(float offset);
QHash<Id, QVector<Highlight>> highlights() const;
void addHighlight(Highlight highlight); void addHighlight(Highlight highlight);
void removeHighlights(Id id); void removeHighlights(Id id);
void removeAllHighlights(); void removeAllHighlights();
bool eventFilter(QObject *, QEvent *event) override;
protected:
void moveEvent(QMoveEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
void changeEvent(QEvent *even) override;
private: private:
QRect overlayRect(); QHash<Id, QVector<Highlight> > m_highlights;
void overlayDestroyed(); float m_visibleRange = 0.0;
float m_rangeOffset = 0.0;
QWidget *m_widget; QAbstractScrollArea *m_scrollArea = nullptr;
HighlightScrollBarOverlay *m_overlay; QPointer<HighlightScrollBarOverlay> m_overlay;
friend class HighlightScrollBarOverlay;
}; };
} // namespace Core } // namespace Core

View File

@@ -108,7 +108,6 @@ DescriptionEditorWidget::DescriptionEditorWidget(QWidget *parent)
DisplaySettings settings = displaySettings(); DisplaySettings settings = displaySettings();
settings.m_textWrapping = false; settings.m_textWrapping = false;
settings.m_displayLineNumbers = false; settings.m_displayLineNumbers = false;
settings.m_highlightCurrentLine = false;
settings.m_displayFoldingMarkers = false; settings.m_displayFoldingMarkers = false;
settings.m_markTextChanges = false; settings.m_markTextChanges = false;
settings.m_highlightBlocks = false; settings.m_highlightBlocks = false;
@@ -143,6 +142,8 @@ void DescriptionEditorWidget::setDisplaySettings(const DisplaySettings &ds)
{ {
DisplaySettings settings = displaySettings(); DisplaySettings settings = displaySettings();
settings.m_visualizeWhitespace = ds.m_visualizeWhitespace; settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
settings.m_scrollBarHighlights = ds.m_scrollBarHighlights;
settings.m_highlightCurrentLine = ds.m_highlightCurrentLine;
TextEditorWidget::setDisplaySettings(settings); TextEditorWidget::setDisplaySettings(settings);
} }

View File

@@ -42,6 +42,7 @@
#include <texteditor/displaysettings.h> #include <texteditor/displaysettings.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/find/highlightscrollbarcontroller.h>
#include <coreplugin/minisplitter.h> #include <coreplugin/minisplitter.h>
#include <utils/tooltip/tooltip.h> #include <utils/tooltip/tooltip.h>
@@ -98,6 +99,8 @@ signals:
int diffFileIndex, int diffFileIndex,
int chunkIndex); int chunkIndex);
void foldChanged(int blockNumber, bool folded); void foldChanged(int blockNumber, bool folded);
void gotDisplaySettings();
void gotFocus();
protected: protected:
int extraAreaWidth(int *markWidthPtr = nullptr) const override { int extraAreaWidth(int *markWidthPtr = nullptr) const override {
@@ -124,6 +127,7 @@ protected:
const QTextBlock &block, const QTextBlock &block,
QPointF offset, QPointF offset,
const QRect &clip) override; const QRect &clip) override;
void focusInEvent(QFocusEvent *e) override;
private: private:
void paintSeparator(QPainter &painter, QColor &color, const QString &text, void paintSeparator(QPainter &painter, QColor &color, const QString &text,
@@ -158,7 +162,6 @@ SideDiffEditorWidget::SideDiffEditorWidget(QWidget *parent)
DisplaySettings settings = displaySettings(); DisplaySettings settings = displaySettings();
settings.m_textWrapping = false; settings.m_textWrapping = false;
settings.m_displayLineNumbers = true; settings.m_displayLineNumbers = true;
settings.m_highlightCurrentLine = false;
settings.m_markTextChanges = false; settings.m_markTextChanges = false;
settings.m_highlightBlocks = false; settings.m_highlightBlocks = false;
SelectableTextEditorWidget::setDisplaySettings(settings); SelectableTextEditorWidget::setDisplaySettings(settings);
@@ -217,7 +220,10 @@ void SideDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds)
DisplaySettings settings = displaySettings(); DisplaySettings settings = displaySettings();
settings.m_visualizeWhitespace = ds.m_visualizeWhitespace; settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
settings.m_displayFoldingMarkers = ds.m_displayFoldingMarkers; settings.m_displayFoldingMarkers = ds.m_displayFoldingMarkers;
settings.m_scrollBarHighlights = ds.m_scrollBarHighlights;
settings.m_highlightCurrentLine = ds.m_highlightCurrentLine;
SelectableTextEditorWidget::setDisplaySettings(settings); SelectableTextEditorWidget::setDisplaySettings(settings);
emit gotDisplaySettings();
} }
void SideDiffEditorWidget::applyFontSettings() void SideDiffEditorWidget::applyFontSettings()
@@ -604,6 +610,12 @@ void SideDiffEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
m_drawCollapsedClip = clip; m_drawCollapsedClip = clip;
} }
void SideDiffEditorWidget::focusInEvent(QFocusEvent *e)
{
SelectableTextEditorWidget::focusInEvent(e);
emit gotFocus();
}
////////////////// //////////////////
SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent) SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
@@ -613,9 +625,6 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
m_leftEditor = new SideDiffEditorWidget(this); m_leftEditor = new SideDiffEditorWidget(this);
m_leftEditor->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_leftEditor->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
m_leftEditor->setReadOnly(true); m_leftEditor->setReadOnly(true);
connect(TextEditorSettings::instance(), &TextEditorSettings::displaySettingsChanged,
m_leftEditor, &SideDiffEditorWidget::setDisplaySettings);
m_leftEditor->setDisplaySettings(TextEditorSettings::displaySettings());
m_leftEditor->setCodeStyle(TextEditorSettings::codeStyle()); m_leftEditor->setCodeStyle(TextEditorSettings::codeStyle());
connect(m_leftEditor, &SideDiffEditorWidget::jumpToOriginalFileRequested, connect(m_leftEditor, &SideDiffEditorWidget::jumpToOriginalFileRequested,
this, &SideBySideDiffEditorWidget::slotLeftJumpToOriginalFileRequested); this, &SideBySideDiffEditorWidget::slotLeftJumpToOriginalFileRequested);
@@ -625,9 +634,6 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
m_rightEditor = new SideDiffEditorWidget(this); m_rightEditor = new SideDiffEditorWidget(this);
m_rightEditor->setReadOnly(true); m_rightEditor->setReadOnly(true);
connect(TextEditorSettings::instance(), &TextEditorSettings::displaySettingsChanged,
m_rightEditor, &SideDiffEditorWidget::setDisplaySettings);
m_rightEditor->setDisplaySettings(TextEditorSettings::displaySettings());
m_rightEditor->setCodeStyle(TextEditorSettings::codeStyle()); m_rightEditor->setCodeStyle(TextEditorSettings::codeStyle());
connect(m_rightEditor, &SideDiffEditorWidget::jumpToOriginalFileRequested, connect(m_rightEditor, &SideDiffEditorWidget::jumpToOriginalFileRequested,
this, &SideBySideDiffEditorWidget::slotRightJumpToOriginalFileRequested); this, &SideBySideDiffEditorWidget::slotRightJumpToOriginalFileRequested);
@@ -635,6 +641,44 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
this, &SideBySideDiffEditorWidget::slotRightContextMenuRequested, this, &SideBySideDiffEditorWidget::slotRightContextMenuRequested,
Qt::DirectConnection); Qt::DirectConnection);
auto setupHighlightController = [this]() {
HighlightScrollBarController *highlightController = m_leftEditor->highlightScrollBarController();
if (highlightController)
highlightController->setScrollArea(m_rightEditor);
};
setupHighlightController();
connect(m_leftEditor, &SideDiffEditorWidget::gotDisplaySettings, setupHighlightController);
m_rightEditor->verticalScrollBar()->setFocusProxy(m_leftEditor);
connect(m_leftEditor, &SideDiffEditorWidget::gotFocus, [this]() {
if (m_rightEditor->verticalScrollBar()->focusProxy() == m_leftEditor)
return; // We already did it before.
// Hack #1. If the left editor got a focus last time
// we don't want to focus right editor when clicking the right
// scrollbar.
m_rightEditor->verticalScrollBar()->setFocusProxy(m_leftEditor);
// Hack #2. If the focus is currently not on the scrollbar's proxy
// and we click on the scrollbar, the focus will go to the parent
// of the scrollbar. In order to give the focus to the proxy
// we need to set a click focus policy on the scrollbar.
// See QApplicationPrivate::giveFocusAccordingToFocusPolicy().
m_rightEditor->verticalScrollBar()->setFocusPolicy(Qt::ClickFocus);
// Hack #3. Setting the focus policy is not orthogonal to setting
// the focus proxy and unfortuantely it changes the policy of the proxy
// too. We bring back the original policy to keep tab focus working.
m_leftEditor->setFocusPolicy(Qt::StrongFocus);
});
connect(m_rightEditor, &SideDiffEditorWidget::gotFocus, [this]() {
// Unhack #1.
m_rightEditor->verticalScrollBar()->setFocusProxy(nullptr);
// Unhack #2.
m_rightEditor->verticalScrollBar()->setFocusPolicy(Qt::NoFocus);
});
connect(TextEditorSettings::instance(), connect(TextEditorSettings::instance(),
&TextEditorSettings::fontSettingsChanged, &TextEditorSettings::fontSettingsChanged,
this, &SideBySideDiffEditorWidget::setFontSettings); this, &SideBySideDiffEditorWidget::setFontSettings);
@@ -678,7 +722,7 @@ SideBySideDiffEditorWidget::SideBySideDiffEditorWidget(QWidget *parent)
QVBoxLayout *l = new QVBoxLayout(this); QVBoxLayout *l = new QVBoxLayout(this);
l->setMargin(0); l->setMargin(0);
l->addWidget(m_splitter); l->addWidget(m_splitter);
setFocusProxy(m_rightEditor); setFocusProxy(m_leftEditor);
m_leftContext = new IContext(this); m_leftContext = new IContext(this);
m_leftContext->setWidget(m_leftEditor); m_leftContext->setWidget(m_leftEditor);
@@ -1060,32 +1104,43 @@ void SideBySideDiffEditorWidget::leftCursorPositionChanged()
{ {
leftVSliderChanged(); leftVSliderChanged();
leftHSliderChanged(); leftHSliderChanged();
handlePositionChange(m_leftEditor, m_rightEditor);
if (m_controller.m_ignoreCurrentIndexChange)
return;
const bool oldIgnore = m_controller.m_ignoreCurrentIndexChange;
m_controller.m_ignoreCurrentIndexChange = true;
emit currentDiffFileIndexChanged(
m_leftEditor->fileIndexForBlockNumber(m_leftEditor->textCursor().blockNumber()));
m_controller.m_ignoreCurrentIndexChange = oldIgnore;
} }
void SideBySideDiffEditorWidget::rightCursorPositionChanged() void SideBySideDiffEditorWidget::rightCursorPositionChanged()
{ {
rightVSliderChanged(); rightVSliderChanged();
rightHSliderChanged(); rightHSliderChanged();
handlePositionChange(m_rightEditor, m_leftEditor);
}
void SideBySideDiffEditorWidget::handlePositionChange(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
{
if (m_controller.m_ignoreCurrentIndexChange) if (m_controller.m_ignoreCurrentIndexChange)
return; return;
const bool oldIgnore = m_controller.m_ignoreCurrentIndexChange; const bool oldIgnore = m_controller.m_ignoreCurrentIndexChange;
m_controller.m_ignoreCurrentIndexChange = true; m_controller.m_ignoreCurrentIndexChange = true;
syncCursor(source, dest);
emit currentDiffFileIndexChanged( emit currentDiffFileIndexChanged(
m_rightEditor->fileIndexForBlockNumber(m_rightEditor->textCursor().blockNumber())); source->fileIndexForBlockNumber(source->textCursor().blockNumber()));
m_controller.m_ignoreCurrentIndexChange = oldIgnore; m_controller.m_ignoreCurrentIndexChange = oldIgnore;
} }
void SideBySideDiffEditorWidget::syncCursor(SideDiffEditorWidget *source, SideDiffEditorWidget *dest)
{
const QTextCursor sourceCursor = source->textCursor();
const int sourceLine = sourceCursor.blockNumber();
const int sourceColumn = sourceCursor.positionInBlock();
QTextCursor destCursor = dest->textCursor();
const QTextBlock destBlock = dest->document()->findBlockByNumber(sourceLine);
const int destColumn = qMin(sourceColumn, destBlock.length());
const int destPosition = destBlock.position() + destColumn;
destCursor.setPosition(destPosition);
dest->setTextCursor(destCursor);
}
} // namespace Internal } // namespace Internal
} // namespace DiffEditor } // namespace DiffEditor

View File

@@ -95,6 +95,8 @@ private:
void rightHSliderChanged(); void rightHSliderChanged();
void leftCursorPositionChanged(); void leftCursorPositionChanged();
void rightCursorPositionChanged(); void rightCursorPositionChanged();
void handlePositionChange(SideDiffEditorWidget *source, SideDiffEditorWidget *dest);
void syncCursor(SideDiffEditorWidget *source, SideDiffEditorWidget *dest);
void showDiff(); void showDiff();

View File

@@ -58,7 +58,6 @@ UnifiedDiffEditorWidget::UnifiedDiffEditorWidget(QWidget *parent)
DisplaySettings settings = displaySettings(); DisplaySettings settings = displaySettings();
settings.m_textWrapping = false; settings.m_textWrapping = false;
settings.m_displayLineNumbers = true; settings.m_displayLineNumbers = true;
settings.m_highlightCurrentLine = false;
settings.m_markTextChanges = false; settings.m_markTextChanges = false;
settings.m_highlightBlocks = false; settings.m_highlightBlocks = false;
SelectableTextEditorWidget::setDisplaySettings(settings); SelectableTextEditorWidget::setDisplaySettings(settings);
@@ -133,6 +132,8 @@ void UnifiedDiffEditorWidget::setDisplaySettings(const DisplaySettings &ds)
DisplaySettings settings = displaySettings(); DisplaySettings settings = displaySettings();
settings.m_visualizeWhitespace = ds.m_visualizeWhitespace; settings.m_visualizeWhitespace = ds.m_visualizeWhitespace;
settings.m_displayFoldingMarkers = ds.m_displayFoldingMarkers; settings.m_displayFoldingMarkers = ds.m_displayFoldingMarkers;
settings.m_scrollBarHighlights = ds.m_scrollBarHighlights;
settings.m_highlightCurrentLine = ds.m_highlightCurrentLine;
SelectableTextEditorWidget::setDisplaySettings(settings); SelectableTextEditorWidget::setDisplaySettings(settings);
} }

View File

@@ -72,7 +72,7 @@
#include <coreplugin/infobar.h> #include <coreplugin/infobar.h>
#include <coreplugin/manhattanstyle.h> #include <coreplugin/manhattanstyle.h>
#include <coreplugin/find/basetextfind.h> #include <coreplugin/find/basetextfind.h>
#include <coreplugin/find/highlightscrollbar.h> #include <coreplugin/find/highlightscrollbarcontroller.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/asconst.h> #include <utils/asconst.h>
#include <utils/textutils.h> #include <utils/textutils.h>
@@ -759,7 +759,7 @@ public:
QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr; QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr;
QVector<SearchResult> m_searchResults; QVector<SearchResult> m_searchResults;
QTimer m_scrollBarUpdateTimer; QTimer m_scrollBarUpdateTimer;
HighlightScrollBar *m_highlightScrollBar = nullptr; HighlightScrollBarController *m_highlightScrollBarController = nullptr;
bool m_scrollBarUpdateScheduled = false; bool m_scrollBarUpdateScheduled = false;
}; };
@@ -814,6 +814,7 @@ TextEditorWidgetPrivate::~TextEditorWidgetPrivate()
this, &TextEditorWidgetPrivate::markRemoved); this, &TextEditorWidgetPrivate::markRemoved);
q->disconnect(this); q->disconnect(this);
delete m_toolBar; delete m_toolBar;
delete m_highlightScrollBarController;
} }
} // namespace Internal } // namespace Internal
@@ -904,15 +905,15 @@ void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc)
void TextEditorWidgetPrivate::setupScrollBar() void TextEditorWidgetPrivate::setupScrollBar()
{ {
if (m_displaySettings.m_scrollBarHighlights) { if (m_displaySettings.m_scrollBarHighlights) {
if (m_highlightScrollBar) if (!m_highlightScrollBarController)
return; m_highlightScrollBarController = new HighlightScrollBarController();
m_highlightScrollBar = new HighlightScrollBar(Qt::Vertical, q);
q->setVerticalScrollBar(m_highlightScrollBar); m_highlightScrollBarController->setScrollArea(q);
highlightSearchResultsInScrollBar(); highlightSearchResultsInScrollBar();
scheduleUpdateHighlightScrollBar(); scheduleUpdateHighlightScrollBar();
} else if (m_highlightScrollBar) { } else if (m_highlightScrollBarController) {
q->setVerticalScrollBar(new QScrollBar(Qt::Vertical, q)); delete m_highlightScrollBarController;
m_highlightScrollBar = 0; m_highlightScrollBarController = nullptr;
} }
} }
@@ -5311,14 +5312,14 @@ void TextEditorWidgetPrivate::updateHighlights()
void TextEditorWidgetPrivate::updateCurrentLineInScrollbar() void TextEditorWidgetPrivate::updateCurrentLineInScrollbar()
{ {
if (m_highlightCurrentLine && m_highlightScrollBar) { if (m_highlightCurrentLine && m_highlightScrollBarController) {
m_highlightScrollBar->removeHighlights(Constants::SCROLL_BAR_CURRENT_LINE); m_highlightScrollBarController->removeHighlights(Constants::SCROLL_BAR_CURRENT_LINE);
if (m_highlightScrollBar->maximum() > 0) { if (m_highlightScrollBarController->scrollBar()->maximum() > 0) {
const QTextCursor &tc = q->textCursor(); const QTextCursor &tc = q->textCursor();
if (QTextLayout *layout = tc.block().layout()) { if (QTextLayout *layout = tc.block().layout()) {
const int pos = q->textCursor().block().firstLineNumber() + const int pos = q->textCursor().block().firstLineNumber() +
layout->lineForTextPosition(tc.positionInBlock()).lineNumber(); layout->lineForTextPosition(tc.positionInBlock()).lineNumber();
m_highlightScrollBar->addHighlight({Constants::SCROLL_BAR_CURRENT_LINE, pos, m_highlightScrollBarController->addHighlight({Constants::SCROLL_BAR_CURRENT_LINE, pos,
Theme::TextEditor_CurrentLine_ScrollBarColor, Theme::TextEditor_CurrentLine_ScrollBarColor,
Highlight::HighestPriority}); Highlight::HighestPriority});
} }
@@ -6213,7 +6214,7 @@ void TextEditorWidgetPrivate::highlightSearchResultsSlot(const QString &txt, Fin
m_delayedUpdateTimer.start(50); m_delayedUpdateTimer.start(50);
if (m_highlightScrollBar) if (m_highlightScrollBarController)
m_scrollBarUpdateTimer.start(50); m_scrollBarUpdateTimer.start(50);
} }
@@ -6241,22 +6242,22 @@ void TextEditorWidgetPrivate::searchFinished()
void TextEditorWidgetPrivate::adjustScrollBarRanges() void TextEditorWidgetPrivate::adjustScrollBarRanges()
{ {
if (!m_highlightScrollBar) if (!m_highlightScrollBarController)
return; return;
const float lineSpacing = QFontMetricsF(q->font()).lineSpacing(); const float lineSpacing = QFontMetricsF(q->font()).lineSpacing();
if (lineSpacing == 0) if (lineSpacing == 0)
return; return;
const float offset = q->contentOffset().y(); const float offset = q->contentOffset().y();
m_highlightScrollBar->setVisibleRange((q->viewport()->rect().height() - offset) / lineSpacing); m_highlightScrollBarController->setVisibleRange((q->viewport()->rect().height() - offset) / lineSpacing);
m_highlightScrollBar->setRangeOffset(offset / lineSpacing); m_highlightScrollBarController->setRangeOffset(offset / lineSpacing);
} }
void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar() void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar()
{ {
if (!m_highlightScrollBar) if (!m_highlightScrollBarController)
return; return;
m_highlightScrollBar->removeHighlights(Constants::SCROLL_BAR_SEARCH_RESULT); m_highlightScrollBarController->removeHighlights(Constants::SCROLL_BAR_SEARCH_RESULT);
m_searchResults.clear(); m_searchResults.clear();
if (m_searchWatcher) { if (m_searchWatcher) {
@@ -6318,7 +6319,7 @@ Highlight::Priority textMarkPrioToScrollBarPrio(const TextMark::Priority &prio)
void TextEditorWidgetPrivate::addSearchResultsToScrollBar(QVector<SearchResult> results) void TextEditorWidgetPrivate::addSearchResultsToScrollBar(QVector<SearchResult> results)
{ {
if (!m_highlightScrollBar) if (!m_highlightScrollBarController)
return; return;
foreach (SearchResult result, results) { foreach (SearchResult result, results) {
const QTextBlock &block = q->document()->findBlock(result.start); const QTextBlock &block = q->document()->findBlock(result.start);
@@ -6326,7 +6327,7 @@ void TextEditorWidgetPrivate::addSearchResultsToScrollBar(QVector<SearchResult>
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber(); const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();
const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber(); const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber();
for (int line = firstLine; line <= lastLine; ++line) { for (int line = firstLine; line <= lastLine; ++line) {
m_highlightScrollBar->addHighlight( m_highlightScrollBarController->addHighlight(
{Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line, {Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line,
Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority}); Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority});
} }
@@ -6343,10 +6344,10 @@ Highlight markToHighlight(TextMark *mark, int lineNumber)
void TextEditorWidgetPrivate::updateHighlightScrollBarNow() void TextEditorWidgetPrivate::updateHighlightScrollBarNow()
{ {
m_scrollBarUpdateScheduled = false; m_scrollBarUpdateScheduled = false;
if (!m_highlightScrollBar) if (!m_highlightScrollBarController)
return; return;
m_highlightScrollBar->removeAllHighlights(); m_highlightScrollBarController->removeAllHighlights();
updateCurrentLineInScrollbar(); updateCurrentLineInScrollbar();
@@ -6359,7 +6360,7 @@ void TextEditorWidgetPrivate::updateHighlightScrollBarNow()
continue; continue;
const QTextBlock &block = q->document()->findBlockByNumber(mark->lineNumber() - 1); const QTextBlock &block = q->document()->findBlockByNumber(mark->lineNumber() - 1);
if (block.isVisible()) if (block.isVisible())
m_highlightScrollBar->addHighlight(markToHighlight(mark, block.firstLineNumber())); m_highlightScrollBarController->addHighlight(markToHighlight(mark, block.firstLineNumber()));
} }
} }
@@ -8376,6 +8377,11 @@ int TextEditorWidget::centerVisibleLine() const
return block.isValid() ? block.blockNumber() : -1; return block.isValid() ? block.blockNumber() : -1;
} }
HighlightScrollBarController *TextEditorWidget::highlightScrollBarController() const
{
return d->m_highlightScrollBarController;
}
bool TextEditorWidget::isMissingSyntaxDefinition() const bool TextEditorWidget::isMissingSyntaxDefinition() const
{ {
return d->m_isMissingSyntaxDefinition; return d->m_isMissingSyntaxDefinition;

View File

@@ -51,6 +51,10 @@ class QRect;
class QTextBlock; class QTextBlock;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace Core {
class HighlightScrollBarController;
}
namespace TextEditor { namespace TextEditor {
class TextDocument; class TextDocument;
class BaseHoverHandler; class BaseHoverHandler;
@@ -458,6 +462,8 @@ public:
/*! Returns the line visible closest to the vertical center of the editor. */ /*! Returns the line visible closest to the vertical center of the editor. */
int centerVisibleLine() const; int centerVisibleLine() const;
Core::HighlightScrollBarController *highlightScrollBarController() const;
signals: signals:
void assistFinished(); // Used in tests. void assistFinished(); // Used in tests.
void readOnlyChanged(); void readOnlyChanged();