Commit e921deff authored by Tomaz  Canabrava's avatar Tomaz Canabrava

Start the work to move to QTabBar

parent b579ee5c
......@@ -82,7 +82,6 @@ target_sources(
katerunninginstanceinfo.cpp
katesavemodifieddialog.cpp
katetabbar.cpp
katetabbutton.cpp
kateviewmanager.cpp
kateviewspace.cpp
katewaiter.cpp
......
This diff is collapsed.
......@@ -21,14 +21,12 @@
#ifndef KATE_TAB_BAR_H
#define KATE_TAB_BAR_H
#include <QWidget>
#include <QTabBar>
#include <QUrl>
#include <QHash>
#include <QIcon>
#include <QVector>
class KateTabButton;
class KateTabBarPrivate;
namespace KTextEditor {
class Document;
}
/**
* The \p KateTabBar class provides a tab bar, e.g. for tabbed documents.
......@@ -37,7 +35,7 @@ class KateTabBarPrivate;
*
* @author Dominik Haumann
*/
class KateTabBar : public QWidget
class KateTabBar : public QTabBar
{
Q_OBJECT
Q_PROPERTY(bool isActive READ isActive WRITE setActive)
......@@ -46,29 +44,6 @@ public:
explicit KateTabBar(QWidget *parent = nullptr);
~KateTabBar() override;
/**
* Adds a new tab with \a text. Returns the new tab's id.
*/
int addTab(const QString &text);
/**
* Insert a tab at \p position with \a text. Returns the new tab's id.
* @param position index of the tab, i.e. 0, ..., count()
* @param text the text snippet
*/
int insertTab(int position, const QString &text);
/**
* Removes the tab with ID \a id.
* @return the position where the tab was
*/
int removeTab(int index);
/**
* Get the ID of the tab bar's activated tab. Returns -1 if no tab is activated.
*/
int currentTab() const;
/**
* Get the ID of the tab that is located left of the current tab.
* The return value is -1, if there is no previous tab.
......@@ -81,40 +56,11 @@ public:
*/
int nextTab() const;
public Q_SLOTS:
/**
* Activate the tab with \p id. No signal is emitted.
*/
void setCurrentTab(int index); // does not emit signal
public:
/**
* Returns whether a tab with ID \a id exists.
*/
bool containsTab(int index) const;
/**
* Set the button @p id's tool tip to @p tip.
*/
void setTabToolTip(int index, const QString &tip);
/**
* Get the button @p id's url. Result is QStrint() if not available.
*/
QString tabToolTip(int index) const;
/**
* Sets the text of the tab with ID \a id to \a text.
* \see tabText()
*/
void setTabText(int index, const QString &text);
/**
* Returns the text of the tab with ID \a id. If the button id does not
* exist \a QString() is returned.
* \see setTabText()
*/
QString tabText(int index) const;
/**
* Sets the URL of the tab with ID \a id to \a url.
......@@ -129,24 +75,14 @@ public:
* \see setTabUrl()
* \since 17.08
*/
QUrl tabUrl(int index) const;
QUrl tabUrl(int index);
/**
* Sets the icon of the tab with ID \a id to \a icon.
* \see tabIcon()
*/
void setTabIcon(int index, const QIcon &pixmap);
/**
* Returns the icon of the tab with ID \a id. If the button id does not
* exist \a QIcon() is returned.
* \see setTabIcon()
*/
QIcon tabIcon(int index) const;
QVariant ensureValidTabData(int idx);
/**
* Returns the number of tabs in the tab bar.
*/
int count() const;
void setCurrentDocument(KTextEditor::Document *doc);
int documentIdx(KTextEditor::Document *doc);
void setTabDocument(int idx, KTextEditor::Document *doc);
KTextEditor::Document *tabDocument(int idx);
/**
* Return the maximum amount of tabs that fit into the tab bar given
......@@ -170,16 +106,6 @@ public:
bool isActive() const;
Q_SIGNALS:
/**
* This signal is emitted whenever the current activated tab changes.
*/
void currentChanged(int id);
/**
* This signal is emitted whenever tab @p id should be closed.
*/
void closeTabRequested(int id);
/**
* This signal is emitted whenever the context menu is requested for
* button @p id at position @p globalPos.
......@@ -216,46 +142,21 @@ Q_SIGNALS:
*/
void activateViewSpaceRequested();
protected Q_SLOTS:
/**
* Active button changed. Emit signal \p currentChanged() with the button's ID.
*/
void tabButtonActivated(KateTabButton *tabButton);
/**
* If the user wants to close a tab with the context menu, it sends a close
* request.
*/
void tabButtonCloseRequest(KateTabButton *tabButton);
protected:
//! Recalculate geometry for all tabs.
void resizeEvent(QResizeEvent *event) override;
//! Override to avoid requesting a new tab.
void mouseDoubleClickEvent(QMouseEvent *event) override;
//! Override to request making the tab bar active.
void mousePressEvent(QMouseEvent *event) override;
//! trigger repaint on hover leave event
void leaveEvent(QEvent *event) override;
//! Paint tab separators
void paintEvent(QPaintEvent *event) override;
//! Request context menu
void contextMenuEvent(QContextMenuEvent *ev) override;
//! Cycle through tabs
void wheelEvent(QWheelEvent *event) override;
//! Support for drag & drop of tabs
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
private:
class KateTabBarPrivate;
// pimpl data holder
KateTabBarPrivate *const d;
};
......
/* SPDX-License-Identifier: LGPL-2.0-or-later
Copyright (C) 2014 Dominik Haumann <dhaumann@kde.org>
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 "katetabbutton.h"
#include <KLocalizedString>
#include <QApplication>
// #include <QDebug>
#include <QDrag>
#include <QFontDatabase>
#include <QHBoxLayout>
#include <QMimeData>
#include <QMoveEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyle>
#include <QStyleOption>
#include <math.h>
TabCloseButton::TabCloseButton(QWidget *parent)
: QPushButton(parent)
{
// should never have focus
setFocusPolicy(Qt::NoFocus);
// closing a tab closes the document
setToolTip(i18n("Close Document"));
setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
}
void TabCloseButton::paintEvent(QPaintEvent *)
{
// get the tab this close button belongs to
KateTabButton *tabButton = qobject_cast<KateTabButton *>(parent());
const bool isActive = underMouse() || (tabButton && tabButton->isChecked());
QStyleOptionButton opt;
opt.initFrom(this);
opt.icon = icon();
opt.iconSize = sizeHint();
// removes the QStyleOptionButton::HasMenu ButtonFeature
opt.features = QStyleOptionButton::Flat;
opt.state.setFlag(QStyle::State_Enabled, isActive);
opt.state.setFlag(QStyle::State_MouseOver, false);
QPainter painter(this);
style()->drawControl(QStyle::CE_PushButton, &opt, &painter, this);
}
QSize TabCloseButton::sizeHint() const
{
// make sure the widget is polished
ensurePolished();
// read the metrics from the style
const int w = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, nullptr, this);
const int h = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, nullptr, this);
return QSize(w, h);
}
void TabCloseButton::enterEvent(QEvent *event)
{
update(); // repaint on hover
QPushButton::enterEvent(event);
}
void TabCloseButton::leaveEvent(QEvent *event)
{
update(); // repaint on hover
QPushButton::leaveEvent(event);
}
KateTabButton::KateTabButton(const QString &text, QWidget *parent)
: QAbstractButton(parent)
, m_geometryAnimation(nullptr)
{
setCheckable(true);
setFocusPolicy(Qt::NoFocus);
setText(text);
// add close button
const int margin = style()->pixelMetric(QStyle::PM_ButtonMargin, nullptr, this);
m_closeButton = new TabCloseButton(this);
QHBoxLayout *hbox = new QHBoxLayout(this);
hbox->setSpacing(0);
hbox->setContentsMargins(margin, 0, margin, 0);
hbox->addStretch();
hbox->addWidget(m_closeButton);
setLayout(hbox);
connect(m_closeButton, &TabCloseButton::clicked, this, &KateTabButton::closeButtonClicked);
}
void KateTabButton::closeButtonClicked()
{
emit closeRequest(this);
}
void KateTabButton::paintEvent(QPaintEvent *ev)
{
Q_UNUSED(ev)
QColor barColor(palette().color(QPalette::Highlight));
// read from the parent widget (=KateTabBar) the isActiveTabBar property
if (!isActiveTabBar()) {
// if inactive, convert color to gray value
const int g = qGray(barColor.rgb());
barColor = QColor(g, g, g);
}
// compute sane margins
const int margin = style()->pixelMetric(QStyle::PM_ButtonMargin, nullptr, this);
const int barMargin = margin / 2;
const int barHeight = ceil(height() / 10.0);
QPainter p(this);
// paint bar if inactive but hovered
if (!isChecked() && underMouse()) {
barColor.setAlpha(80);
p.fillRect(QRect(barMargin, height() - barHeight, width() - 2 * barMargin, barHeight), barColor);
}
// paint bar
if (isChecked()) {
barColor.setAlpha(255);
p.fillRect(QRect(barMargin, height() - barHeight, width() - 2 * barMargin, barHeight), barColor);
}
// icon, if applicable
int leftMargin = margin;
if (!icon().isNull()) {
const int y = (height() - 16) / 2;
icon().paint(&p, margin, y, 16, 16);
leftMargin += 16;
leftMargin += margin;
}
// the width of the text is reduced by the close button + 2 * margin
const int w = width() // width of widget
- m_closeButton->width() - 2 * margin // close button
- leftMargin; // modified button
// draw text, we need to elide to xxx...xxx is too long
const QString elidedText = QFontMetrics(font()).elidedText(text(), Qt::ElideMiddle, w);
QRect textRect(leftMargin, 0, w, height());
if (layoutDirection() == Qt::RightToLeft) {
textRect.moveLeft(textRect.left() + 16);
}
const QPalette pal = QApplication::palette();
style()->drawItemText(&p, textRect, Qt::AlignHCenter | Qt::AlignVCenter, pal, true, elidedText, QPalette::WindowText);
}
void KateTabButton::mousePressEvent(QMouseEvent *ev)
{
ev->accept();
// save mouse position for possible drag start event
m_mouseDownPosition = ev->globalPos();
// activation code
if (ev->button() == Qt::LeftButton) {
if (!isChecked()) {
// make sure we stay checked
setChecked(true);
}
// notify that this button was activated
emit activated(this);
} else if (ev->button() == Qt::MiddleButton) {
emit closeRequest(this);
} else {
ev->ignore();
}
}
void KateTabButton::mouseMoveEvent(QMouseEvent *event)
{
// possibly start drag event
if (QPoint(event->globalPos() - m_mouseDownPosition).manhattanLength() > QApplication::startDragDistance()) {
QMimeData *mimeData = new QMimeData;
mimeData->setData(QStringLiteral("application/x-dndkatetabbutton"), QByteArray());
mimeData->setUrls({m_url});
auto drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setDragCursor(QPixmap(), Qt::MoveAction);
drag->exec(Qt::MoveAction);
event->accept();
}
QAbstractButton::mouseMoveEvent(event);
}
void KateTabButton::mouseDoubleClickEvent(QMouseEvent *event)
{
event->accept();
}
void KateTabButton::enterEvent(QEvent *event)
{
update(); // repaint on hover
QAbstractButton::enterEvent(event);
}
void KateTabButton::leaveEvent(QEvent *event)
{
update(); // repaint on hover
QAbstractButton::leaveEvent(event);
}
void KateTabButton::moveEvent(QMoveEvent *event)
{
// tell the tabbar to redraw its separators. Since the separators overlap
// the tab buttons geometry, we need to adjust the width by the separator's
// width to avoid artifacts
if (parentWidget()) {
const int w = style()->pixelMetric(QStyle::PM_ToolBarSeparatorExtent, nullptr, this);
QRect rect = geometry();
rect.moveLeft(event->oldPos().x());
rect.adjust(-w, 0, w, 0);
parentWidget()->update(rect);
}
QAbstractButton::moveEvent(event);
}
bool KateTabButton::isActiveTabBar() const
{
Q_ASSERT(parentWidget());
// read from the parent widget (=KateTabBar) the isActive property
return parentWidget()->property("isActive").toBool();
}
void KateTabButton::setAnimatedGeometry(const QRect &startGeom, const QRect &endGeom)
{
// stop animation in case it is running
if (m_geometryAnimation && m_geometryAnimation->state() != QAbstractAnimation::Stopped) {
m_geometryAnimation->stop();
}
// already at desired position
if (startGeom == geometry() && endGeom == startGeom) {
return;
}
// if the style does not want animations, just set geometry
if (!style()->styleHint(QStyle::SH_Widget_Animate, nullptr, this) || (isVisible() && endGeom == startGeom)) {
setGeometry(endGeom);
return;
}
if (!m_geometryAnimation) {
m_geometryAnimation = new QPropertyAnimation(this, "geometry", this);
m_geometryAnimation->setDuration(100);
}
// finally start geometry animation
m_geometryAnimation->setStartValue(startGeom);
m_geometryAnimation->setEndValue(endGeom);
m_geometryAnimation->start();
}
bool KateTabButton::geometryAnimationRunning() const
{
return m_geometryAnimation && (m_geometryAnimation->state() != QAbstractAnimation::Stopped);
}
QUrl KateTabButton::url() const
{
return m_url;
}
void KateTabButton::setUrl(const QUrl &url)
{
m_url = url;
}
/* SPDX-License-Identifier: LGPL-2.0-or-later
Copyright (C) 2014 Dominik Haumann <dhaumann@kde.org>
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_TAB_BUTTON
#define KATE_TAB_BUTTON
#include <QPushButton>
#include <QUrl>
class QPropertyAnimation;
class TabCloseButton : public QPushButton
{
Q_OBJECT
public:
// constructor
TabCloseButton(QWidget *parent = nullptr);
// paint close button depending on its state
void paintEvent(QPaintEvent *event) override;
// returns the size hint depending on the style
QSize sizeHint() const override;
protected:
void enterEvent(QEvent *event) override;
void leaveEvent(QEvent *event) override;
};
/**
* A \p KateTabButton represents a button on the tab bar. It can either be
* \e activated or \e deactivated. If the state is \e deactivated it will
* be @e activated when the mouse is pressed. It then emits the signal
* @p activated(). The \p KateTabButton's text can be set with \p setText()
* and an additional icon can be shown with \p setIcon().
*
* @author Dominik Haumann
*/
class KateTabButton : public QAbstractButton
{
Q_OBJECT
public:
/**
* Constructs a new tab bar button with \a text and \a parent.
*/
KateTabButton(const QString &text, QWidget *parent = nullptr);
/**
* Returns @e true, if the tabbar is the currently active tab bar.
*/
bool isActiveTabBar() const;
/**
* Check whether a geometry animation is running.
*/
bool geometryAnimationRunning() const;
/**
* The URL of the document this tab represents.
* \since 17.08
*/
QUrl url() const;
/**
* Sets the URL of the document this tab represents.
* \since 17.08
*/
void setUrl(const QUrl &url);
public Q_SLOTS:
/**
* Animate the button's geometry from @p startGeom to @p endGeom
* with linear interpolation.
*/
void setAnimatedGeometry(const QRect &startGeom, const QRect &endGeom);
Q_SIGNALS:
/**
* Emitted whenever the button changes state from deactivated to activated,
* or when the button was clicked although it was already active.
* @param tabbutton the pressed button (this)
*/
void activated(KateTabButton *tabbutton);
/**
* Emitted whenever the user wants to close the tab button.
* @param tabbutton the button that emitted this signal
*/
void closeRequest(KateTabButton *tabbutton);
protected Q_SLOTS:
void closeButtonClicked();
protected:
/** paint eyecandy rectangles around the button */
void paintEvent(QPaintEvent *ev) override;
/** activate, and for drag & drop */
void mousePressEvent(QMouseEvent *ev) override;
/** possibly start drag event */
void mouseMoveEvent(QMouseEvent *event) override;
/** eat double click events */
void mouseDoubleClickEvent(QMouseEvent *event) override;
/** trigger repaint on hover enter event */
void enterEvent(QEvent *event) override;
/** trigger repaint on hover leave event */
void leaveEvent(QEvent *event) override;
/** track geometry changes to trigger proper repaint*/
void moveEvent(QMoveEvent *event) override;
private:
TabCloseButton *m_closeButton;
QPropertyAnimation *m_geometryAnimation;
QPoint m_mouseDownPosition;
QUrl m_url;
};
#endif
......@@ -65,7 +65,7 @@ KateViewSpace::KateViewSpace(KateViewManager *viewManager, QWidget *parent, cons
connect(m_tabBar, &KateTabBar::currentChanged, this, &KateViewSpace::changeView);
connect(m_tabBar, &KateTabBar::moreTabsRequested, this, &KateViewSpace::addTabs);
connect(m_tabBar, &KateTabBar::lessTabsRequested, this, &KateViewSpace::removeTabs);
connect(m_tabBar, &KateTabBar::closeTabRequested, this, &KateViewSpace::closeTabRequest, Qt::QueuedConnection);
connect(m_tabBar, &KateTabBar::tabCloseRequested, this, &KateViewSpace::closeTabRequest, Qt::QueuedConnection);
connect(m_tabBar, &KateTabBar::contextMenuRequest, this, &KateViewSpace::showContextMenu, Qt::QueuedConnection);
connect(m_tabBar, &KateTabBar::newTabRequested, this, &KateViewSpace::createNewDocument);
connect(m_tabBar, SIGNAL(activateViewSpaceRequested()), this, SLOT(makeActive()));
......@@ -277,34 +277,29 @@ bool KateViewSpace::showView(KTextEditor::Document *document)
kv->show();
// in case a tab does not exist, add one
if (!m_docToTabId.contains(document)) {
if (!m_tabBar->documentIdx(document)) {
// if space is available, add button
if (m_tabBar->count() < m_tabBar->maxTabCount()) {
// just insert
insertTab(0, document);
} else {
if (m_tabBar->count() >= m_tabBar->maxTabCount()) {
// remove "oldest" button and replace with new one