1
mirror of https://invent.kde.org/network/falkon.git synced 2024-12-20 02:36:34 +01:00

Add VerticalTabs plugin

This commit is contained in:
David Rosca 2018-02-02 11:28:29 +01:00
parent af95dd14e2
commit 66023d2dba
No known key found for this signature in database
GPG Key ID: EBC3FC294452C6D8
19 changed files with 1172 additions and 0 deletions

View File

@ -12,6 +12,7 @@ add_subdirectory(MouseGestures)
add_subdirectory(PIM)
add_subdirectory(StatusBarIcons)
add_subdirectory(TabManager)
add_subdirectory(VerticalTabs)
if (GNOME_KEYRING_FOUND)
add_subdirectory(GnomeKeyringPasswords)

View File

@ -0,0 +1,23 @@
set( VerticalTabs_SRCS
verticaltabsplugin.cpp
verticaltabscontroller.cpp
verticaltabswidget.cpp
verticaltabssettings.cpp
tabtreeview.cpp
tabtreedelegate.cpp
loadinganimator.cpp
)
set( VerticalTabs_UIS
verticaltabssettings.ui
)
qt5_wrap_ui(UIS ${VerticalTabs_UIS})
set( VerticalTabs_RSCS
verticaltabs.qrc
)
qt5_add_resources(RSCS ${VerticalTabs_RSCS})
add_library(VerticalTabs MODULE ${VerticalTabs_SRCS} ${UIS} ${RSCS})
install(TARGETS VerticalTabs DESTINATION ${FALKON_INSTALL_PLUGINDIR})
target_link_libraries(VerticalTabs FalkonPrivate)

View File

@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<defs id="defs3051">
<style type="text/css" id="current-color-scheme">
.ColorScheme-Text {
color:#4d4d4d;
}
</style>
</defs>
<path
style="fill:currentColor;fill-opacity:1;stroke:none"
d="M 4.5 3 C 3.671579 3 3.000008 3.67157 3 4.5 C 3.000008 5.32843 3.671579 6 4.5 6 C 5.328421 6 5.999992 5.32843 6 4.5 C 5.999992 3.67157 5.328421 3 4.5 3 z M 7 4 L 7 5 L 8 5 L 8 4 L 7 4 z M 9 4 L 9 5 L 19 5 L 19 4 L 9 4 z M 9 6 L 9 7 L 12 7 L 12 6 L 9 6 z M 4.5 9 C 3.671573 8.9999999 3 9.671572 3 10.5 C 3 11.328428 3.671573 12.000001 4.5 12 C 5.328427 12 6 11.328428 6 10.5 C 6 9.671572 5.328427 8.999999 4.5 9 z M 7 10 L 7 11 L 8 11 L 8 10 L 7 10 z M 9 10 L 9 11 L 19 11 L 19 10 L 9 10 z M 9 12 L 9 13 L 13 13 L 13 12 L 9 12 z M 4.5 15 C 3.671573 15 3 15.671572 3 16.5 C 3 17.328428 3.671573 18.000001 4.5 18 C 5.328427 18 6 17.328428 6 16.5 C 6 15.671572 5.328427 14.999999 4.5 15 z M 7 16 L 7 17 L 8 17 L 8 16 L 7 16 z M 9 16 L 9 17 L 19 17 L 19 16 L 9 16 z M 9 18 L 9 19 L 16 19 L 16 18 L 9 18 z "
class="ColorScheme-Text"
/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,80 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "loadinganimator.h"
#include "tabicon.h"
#include "tabmodel.h"
#include <QTimer>
class LoadingAnimation : public QObject
{
public:
explicit LoadingAnimation(LoadingAnimator *animator)
: QObject(animator)
, m_animator(animator)
{
QTimer *timer = new QTimer(this);
timer->setInterval(TabIcon::data()->animationInterval);
connect(timer, &QTimer::timeout, this, [this]() {
m_currentFrame = (m_currentFrame + 1) % TabIcon::data()->framesCount;
m_animator->updatePixmap(this);
});
timer->start();
}
QPixmap pixmap() const
{
const QPixmap p = TabIcon::data()->animationPixmap;
const int size = 16;
const int pixmapSize = qRound(size * p.devicePixelRatioF());
return p.copy(m_currentFrame * pixmapSize, 0, pixmapSize, pixmapSize);
}
private:
int m_currentFrame = 0;
LoadingAnimator *m_animator;
};
LoadingAnimator::LoadingAnimator(QObject *parent)
: QObject(parent)
{
}
QPixmap LoadingAnimator::pixmap(const QModelIndex &index)
{
LoadingAnimation *animation = m_animations.value(index);
if (!animation) {
animation = new LoadingAnimation(this);
m_indexes[animation] = index;
m_animations[index] = animation;
}
return animation->pixmap();
}
void LoadingAnimator::updatePixmap(LoadingAnimation *animation)
{
const QModelIndex index = m_indexes.value(animation);
if (!index.isValid() || !index.data(TabModel::LoadingRole).toBool()) {
animation->deleteLater();
m_indexes.remove(animation);
m_animations.remove(index);
} else {
emit updateIndex(index);
}
}

View File

@ -0,0 +1,45 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include <QHash>
#include <QObject>
#include <QPersistentModelIndex>
class LoadingAnimation;
class LoadingAnimator : public QObject
{
Q_OBJECT
public:
explicit LoadingAnimator(QObject *parent = nullptr);
QPixmap pixmap(const QModelIndex &index);
signals:
void updateIndex(const QModelIndex &index);
private:
void updatePixmap(LoadingAnimation *animation);
QHash<LoadingAnimation*, QPersistentModelIndex> m_indexes;
QHash<QPersistentModelIndex, LoadingAnimation*> m_animations;
friend class LoadingAnimation;
};

View File

@ -0,0 +1,176 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "tabtreedelegate.h"
#include "tabtreeview.h"
#include "loadinganimator.h"
#include "tabmodel.h"
#include "tabicon.h"
#include "iconprovider.h"
#include <QPainter>
TabTreeDelegate::TabTreeDelegate(TabTreeView *view)
: QStyledItemDelegate()
, m_view(view)
{
m_padding = qMax(5, m_view->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1);
m_indentation = 15;
m_loadingAnimator = new LoadingAnimator(this);
connect(m_loadingAnimator, &LoadingAnimator::updateIndex, this, [this](const QModelIndex &index) {
m_view->update(index);
});
}
static int indexDepth(QModelIndex index)
{
int i = 0;
while (index.parent().isValid()) {
index = index.parent();
i++;
}
return i;
}
QRect TabTreeDelegate::expandButtonRect(const QModelIndex &index) const
{
const QRect rect = m_view->visualRect(index);
const int depth = indexDepth(index);
return QRect(m_indentation * depth, rect.y(), m_indentation, rect.height());
}
QRect TabTreeDelegate::audioButtonRect(const QModelIndex &index) const
{
if (!index.data(TabModel::AudioPlayingRole).toBool() && !index.data(TabModel::AudioMutedRole).toBool()) {
return QRect();
}
const QRect rect = m_view->visualRect(index);
const int center = rect.height() / 2 + rect.top();
const int rightPosition = rect.right() - m_padding * 2 - 16;
return QRect(rightPosition - 16, center - 16 / 2, 16, 16);
}
QRect TabTreeDelegate::closeButtonRect(const QModelIndex &index) const
{
const QRect rect = m_view->visualRect(index);
const int center = rect.height() / 2 + rect.top();
return QRect(rect.right() - m_padding - 16, center - 16 / 2, 16, 16);
}
void TabTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
const QWidget *w = option.widget;
const QStyle *style = w ? w->style() : m_view->style();
const bool reverse = m_view->isRightToLeft();
const bool expanded = m_view->isExpanded(index);
const bool children = m_view->model()->rowCount(index) > 0;
const int depth = indexDepth(index);
QStyleOptionViewItem opt = option;
opt.state.setFlag(QStyle::State_Active, true);
opt.state.setFlag(QStyle::State_HasFocus, false);
opt.state.setFlag(QStyle::State_Selected, index.data(TabModel::CurrentTabRole).toBool());
const int height = opt.rect.height();
const int center = height / 2 + opt.rect.top();
int leftPosition = opt.rect.left() + m_indentation + m_indentation * depth + m_padding;
int rightPosition = opt.rect.right() - m_padding * 2 - 16; // always reserve close button size
const QIcon::Mode iconMode = opt.state & QStyle::State_Selected ? QIcon::Selected : QIcon::Normal;
const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text;
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) {
cg = QPalette::Inactive;
}
#ifdef Q_OS_WIN
opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text));
opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108));
#endif
QPalette textPalette = opt.palette;
textPalette.setCurrentColorGroup(cg);
// Draw background
style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w);
// Draw expand button
if (children) {
QStyle::PrimitiveElement element;
if (expanded) {
element = QStyle::PE_IndicatorArrowDown;
} else {
element = reverse ? QStyle::PE_IndicatorArrowLeft : QStyle::PE_IndicatorArrowRight;
}
QStyleOptionViewItem o = opt;
o.state &= ~QStyle::State_MouseOver;
o.rect.moveLeft(m_indentation * depth);
o.rect.setWidth(m_indentation);
style->drawPrimitive(element, &o, painter, w);
}
// Draw icon
const int iconSize = 16;
const int iconYPos = center - (iconSize / 2);
QRect iconRect(leftPosition, iconYPos, iconSize, iconSize);
QPixmap pixmap;
if (index.data(TabModel::LoadingRole).toBool()) {
pixmap = m_loadingAnimator->pixmap(index);
} else {
pixmap = index.data(Qt::DecorationRole).value<QIcon>().pixmap(iconSize, iconMode);
}
painter->drawPixmap(iconRect, pixmap);
leftPosition += iconRect.width() + m_padding;
// Draw close button
if (opt.state.testFlag(QStyle::State_MouseOver) || opt.state.testFlag(QStyle::State_Selected)) {
QSize closeSize(16, 16);
QPoint pos(opt.rect.right() - m_padding - closeSize.width(), center - closeSize.height() / 2);
QRect closeRect(pos, closeSize);
painter->drawPixmap(closeRect, IconProvider::standardIcon(QStyle::SP_DialogCloseButton).pixmap(closeSize, iconMode));
}
// Draw audio icon
const bool audioMuted = index.data(TabModel::AudioMutedRole).toBool();
const bool audioPlaying = index.data(TabModel::AudioPlayingRole).toBool();
if (audioMuted || audioPlaying) {
QSize audioSize(16, 16);
QPoint pos(rightPosition - audioSize.width(), center - audioSize.height() / 2);
QRect audioRect(pos, audioSize);
painter->drawPixmap(audioRect, audioMuted ? TabIcon::data()->audioMutedPixmap : TabIcon::data()->audioPlayingPixmap);
rightPosition -= audioSize.width() + m_padding;
}
// Draw title
QRect titleRect(leftPosition, center - opt.fontMetrics.height() / 2, opt.rect.width(), opt.fontMetrics.height());
titleRect.setRight(rightPosition - m_padding);
QString title = opt.fontMetrics.elidedText(index.data().toString(), Qt::ElideRight, titleRect.width());
style->drawItemText(painter, titleRect, Qt::AlignLeft, textPalette, true, title, colorRole);
}
QSize TabTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QStyleOptionViewItem opt(option);
initStyleOption(&opt, index);
return QSize(200, m_padding * 2 + opt.fontMetrics.height());
}

View File

@ -0,0 +1,42 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include <QStyledItemDelegate>
class TabTreeView;
class LoadingAnimator;
class TabTreeDelegate : public QStyledItemDelegate
{
public:
explicit TabTreeDelegate(TabTreeView *view);
QRect expandButtonRect(const QModelIndex &index) const;
QRect audioButtonRect(const QModelIndex &index) const;
QRect closeButtonRect(const QModelIndex &index) const;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
private:
TabTreeView *m_view;
LoadingAnimator *m_loadingAnimator;
int m_padding;
int m_indentation;
};

View File

@ -0,0 +1,169 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "tabtreeview.h"
#include "tabtreedelegate.h"
#include "loadinganimator.h"
#include "tabmodel.h"
#include "webtab.h"
#include "tabcontextmenu.h"
#include <QToolTip>
#include <QHoverEvent>
#include <QApplication>
TabTreeView::TabTreeView(QWidget* parent)
: QTreeView(parent)
{
setDragEnabled(true);
setAcceptDrops(true);
setHeaderHidden(true);
setUniformRowHeights(true);
setDropIndicatorShown(true);
setAllColumnsShowFocus(true);
setMouseTracking(true);
setIndentation(0);
m_delegate = new TabTreeDelegate(this);
setItemDelegate(m_delegate);
}
bool TabTreeView::areTabsInOrder() const
{
return m_tabsInOrder;
}
void TabTreeView::setTabsInOrder(bool enable)
{
m_tabsInOrder = enable;
}
void TabTreeView::drawBranches(QPainter *, const QRect &, const QModelIndex &) const
{
// Disable drawing branches
}
void TabTreeView::currentChanged(const QModelIndex &, const QModelIndex &)
{
// Disable current index
setCurrentIndex(QModelIndex());
}
bool TabTreeView::viewportEvent(QEvent *event)
{
switch (event->type()) {
case QEvent::MouseButtonPress: {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
const QModelIndex index = indexAt(me->pos());
WebTab *tab = index.data(TabModel::WebTabRole).value<WebTab*>();
if (me->buttons() == Qt::MiddleButton && tab) {
tab->closeTab();
}
if (me->buttons() != Qt::LeftButton) {
m_pressedIndex = QModelIndex();
m_pressedButton = NoButton;
break;
}
m_pressedIndex = index;
m_pressedButton = buttonAt(me->pos(), m_pressedIndex);
if (m_pressedIndex.isValid()) {
if (m_pressedButton == ExpandButton) {
if (isExpanded(m_pressedIndex)) {
collapse(m_pressedIndex);
} else {
expand(m_pressedIndex);
}
} else if (m_pressedButton == NoButton && tab) {
tab->makeCurrentTab();
}
}
break;
}
case QEvent::MouseButtonRelease: {
QMouseEvent *me = static_cast<QMouseEvent*>(event);
if (me->buttons() != Qt::NoButton) {
break;
}
const QModelIndex index = indexAt(me->pos());
if (m_pressedIndex != index) {
break;
}
DelegateButton button = buttonAt(me->pos(), index);
if (m_pressedButton == button) {
if (m_pressedButton == ExpandButton) {
me->accept();
return true;
}
WebTab *tab = index.data(TabModel::WebTabRole).value<WebTab*>();
if (tab) {
if (m_pressedButton == CloseButton) {
tab->closeTab();
} else if (m_pressedButton == AudioButton) {
tab->toggleMuted();
}
}
}
break;
}
case QEvent::ToolTip: {
QHelpEvent *he = static_cast<QHelpEvent*>(event);
const QModelIndex index = indexAt(he->pos());
DelegateButton button = buttonAt(he->pos(), index);
if (button == AudioButton) {
const bool muted = index.data(TabModel::AudioMutedRole).toBool();
QToolTip::showText(he->globalPos(), muted ? tr("Unmute Tab") : tr("Mute Tab"), this, visualRect(index));
he->accept();
return true;
} else if (button == CloseButton) {
QToolTip::showText(he->globalPos(), tr("Close Tab"), this, visualRect(index));
he->accept();
return true;
}
break;
}
case QEvent::ContextMenu: {
QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event);
const QModelIndex index = indexAt(ce->pos());
WebTab *tab = index.data(TabModel::WebTabRole).value<WebTab*>();
if (tab) {
TabContextMenu menu(tab, Qt::Vertical, m_tabsInOrder);
menu.exec(ce->globalPos());
}
break;
}
default:
break;
}
return QTreeView::viewportEvent(event);
}
TabTreeView::DelegateButton TabTreeView::buttonAt(const QPoint &pos, const QModelIndex &index) const
{
if (m_delegate->expandButtonRect(index).contains(pos)) {
return ExpandButton;
} else if (m_delegate->audioButtonRect(index).contains(pos)) {
return AudioButton;
} else if (m_delegate->closeButtonRect(index).contains(pos)) {
return CloseButton;
}
return NoButton;
}

View File

@ -0,0 +1,53 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include <QTreeView>
class TabTreeDelegate;
class TabTreeView : public QTreeView
{
Q_OBJECT
public:
explicit TabTreeView(QWidget *parent = 0);
// In TabBar order
bool areTabsInOrder() const;
void setTabsInOrder(bool enable);
private:
void drawBranches(QPainter *painter, const QRect &rect, const QModelIndex &index) const override;
void currentChanged(const QModelIndex &current, const QModelIndex &previous) override;
bool viewportEvent(QEvent *event) override;
enum DelegateButton {
NoButton,
ExpandButton,
AudioButton,
CloseButton
};
DelegateButton buttonAt(const QPoint &pos, const QModelIndex &index) const;
TabTreeDelegate *m_delegate;
DelegateButton m_pressedButton = NoButton;
QModelIndex m_pressedIndex;
bool m_tabsInOrder = false;
};

View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/verticaltabs">
<file>data/icon.svg</file>
</qresource>
</RCC>

View File

@ -0,0 +1,49 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "verticaltabscontroller.h"
#include "verticaltabsplugin.h"
#include "verticaltabswidget.h"
#include <QAction>
VerticalTabsController::VerticalTabsController(VerticalTabsPlugin *plugin)
: SideBarInterface(plugin)
, m_plugin(plugin)
{
}
QString VerticalTabsController::title() const
{
return tr("Vertical Tabs");
}
QAction *VerticalTabsController::createMenuAction()
{
QAction *act = new QAction(title(), this);
act->setCheckable(true);
return act;
}
QWidget *VerticalTabsController::createSideBarWidget(BrowserWindow *window)
{
VerticalTabsWidget *widget = new VerticalTabsWidget(window);
connect(m_plugin, &VerticalTabsPlugin::viewTypeChanged, widget, &VerticalTabsWidget::setViewType);
widget->setViewType(m_plugin->viewType());
return widget;
}

View File

@ -0,0 +1,36 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include "sidebarinterface.h"
class VerticalTabsPlugin;
class VerticalTabsController : public SideBarInterface
{
Q_OBJECT
public:
explicit VerticalTabsController(VerticalTabsPlugin *plugin);
QString title() const override;
QAction *createMenuAction() override;
QWidget *createSideBarWidget(BrowserWindow *window) override;
private:
VerticalTabsPlugin *m_plugin;
};

View File

@ -0,0 +1,152 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "verticaltabsplugin.h"
#include "verticaltabssettings.h"
#include "verticaltabscontroller.h"
#include "browserwindow.h"
#include "pluginproxy.h"
#include "mainapplication.h"
#include "tabwidget.h"
#include "tabbar.h"
#include "sidebar.h"
#include "../config.h"
#include <QSettings>
#include <QTranslator>
VerticalTabsPlugin::VerticalTabsPlugin()
: QObject()
{
}
PluginSpec VerticalTabsPlugin::pluginSpec()
{
PluginSpec spec;
spec.name = QSL("Vertical Tabs");
spec.info = QSL("Vertical tabs for Falkon");
spec.description = QSL("Adds ability to show tabs in sidebar");
spec.version = QSL("0.1.0");
spec.author = QSL("David Rosca <nowrep@gmail.com>");
spec.icon = QIcon(QSL(":verticaltabs/data/icon.svg")).pixmap(32);
spec.hasSettings = true;
return spec;
}
void VerticalTabsPlugin::init(InitState state, const QString &settingsPath)
{
m_settingsPath = settingsPath + QL1S("/extensions.ini");
QSettings settings(m_settingsPath, QSettings::IniFormat);
settings.beginGroup(QSL("VerticalTabs"));
m_viewType = static_cast<ViewType>(settings.value(QSL("ViewType"), TabListView).toInt());
m_replaceTabBar = settings.value(QSL("ReplaceTabBar"), false).toBool();
settings.endGroup();
m_controller = new VerticalTabsController(this);
SideBarManager::addSidebar(QSL("VerticalTabs"), m_controller);
connect(mApp->plugins(), &PluginProxy::mainWindowCreated, this, &VerticalTabsPlugin::mainWindowCreated);
if (state == LateInitState) {
const auto windows = mApp->windows();
for (BrowserWindow *window : windows) {
mainWindowCreated(window);
}
}
}
void VerticalTabsPlugin::unload()
{
setTabBarVisible(true);
SideBarManager::removeSidebar(QSL("VerticalTabs"));
delete m_controller;
m_controller = nullptr;
}
bool VerticalTabsPlugin::testPlugin()
{
return (Qz::VERSION == QSL(FALKON_VERSION));
}
QTranslator *VerticalTabsPlugin::getTranslator(const QString &locale)
{
QTranslator *translator = new QTranslator(this);
translator->load(locale, QSL(":/verticaltabs/locale/"));
return translator;
}
void VerticalTabsPlugin::showSettings(QWidget *parent)
{
VerticalTabsSettings *settings = new VerticalTabsSettings(this, parent);
settings->exec();
}
VerticalTabsPlugin::ViewType VerticalTabsPlugin::viewType() const
{
return m_viewType;
}
void VerticalTabsPlugin::setViewType(ViewType type)
{
if (m_viewType == type) {
return;
}
m_viewType = type;
QSettings settings(m_settingsPath, QSettings::IniFormat);
settings.setValue(QSL("VerticalTabs/ViewType"), m_viewType);
emit viewTypeChanged(m_viewType);
}
bool VerticalTabsPlugin::replaceTabBar() const
{
return m_replaceTabBar;
}
void VerticalTabsPlugin::setReplaceTabBar(bool replace)
{
if (m_replaceTabBar == replace) {
return;
}
m_replaceTabBar = replace;
setTabBarVisible(!m_replaceTabBar);
QSettings settings(m_settingsPath, QSettings::IniFormat);
settings.setValue(QSL("VerticalTabs/ReplaceTabBar"), m_replaceTabBar);
}
void VerticalTabsPlugin::mainWindowCreated(BrowserWindow *window)
{
if (window->sideBarManager()->activeSideBar().isEmpty()) {
window->sideBarManager()->showSideBar(QSL("VerticalTabs"));
}
setTabBarVisible(!m_replaceTabBar);
}
void VerticalTabsPlugin::setTabBarVisible(bool visible)
{
const auto windows = mApp->windows();
for (BrowserWindow *window : windows) {
window->tabWidget()->tabBar()->setForceHidden(!visible);
}
}

View File

@ -0,0 +1,64 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include "plugininterface.h"
class BrowserWindow;
class VerticalTabsController;
class VerticalTabsPlugin : public QObject, public PluginInterface
{
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "Falkon.Browser.plugin.VerticalTabs")
public:
explicit VerticalTabsPlugin();
PluginSpec pluginSpec() override;
void init(InitState state, const QString &settingsPath) override;
void unload() override;
bool testPlugin() override;
QTranslator* getTranslator(const QString &locale) override;
void showSettings(QWidget *parent = nullptr) override;
enum ViewType {
TabListView,
TabTreeView
};
ViewType viewType() const;
void setViewType(ViewType type);
bool replaceTabBar() const;
void setReplaceTabBar(bool replace);
signals:
void viewTypeChanged(ViewType type);
private:
void mainWindowCreated(BrowserWindow *window);
void setTabBarVisible(bool visible);
QString m_settingsPath;
VerticalTabsController *m_controller = nullptr;
ViewType m_viewType = TabListView;
bool m_replaceTabBar = false;
};

View File

@ -0,0 +1,45 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "verticaltabssettings.h"
#include "ui_verticaltabssettings.h"
#include "verticaltabsplugin.h"
VerticalTabsSettings::VerticalTabsSettings(VerticalTabsPlugin *plugin, QWidget *parent)
: QDialog(parent)
, ui(new Ui::VerticalTabsSettings)
, m_plugin(plugin)
{
setAttribute(Qt::WA_DeleteOnClose);
ui->setupUi(this);
ui->tabListView->setChecked(m_plugin->viewType() == VerticalTabsPlugin::TabListView);
ui->tabTreeView->setChecked(m_plugin->viewType() == VerticalTabsPlugin::TabTreeView);
ui->replaceTabBar->setChecked(m_plugin->replaceTabBar());
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, [this]() {
m_plugin->setViewType(ui->tabListView->isChecked() ? VerticalTabsPlugin::TabListView : VerticalTabsPlugin::TabTreeView);
m_plugin->setReplaceTabBar(ui->replaceTabBar->isChecked());
accept();
});
}
VerticalTabsSettings::~VerticalTabsSettings()
{
delete ui;
}

View File

@ -0,0 +1,38 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include <QDialog>
namespace Ui {
class VerticalTabsSettings;
}
class VerticalTabsPlugin;
class VerticalTabsSettings : public QDialog
{
Q_OBJECT
public:
explicit VerticalTabsSettings(VerticalTabsPlugin *plugin, QWidget *parent = nullptr);
~VerticalTabsSettings();
private:
Ui::VerticalTabsSettings *ui;
VerticalTabsPlugin *m_plugin;
};

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VerticalTabsSettings</class>
<widget class="QDialog" name="VerticalTabsSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>415</width>
<height>231</height>
</rect>
</property>
<property name="windowTitle">
<string>Vertical Tabs Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>View</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Please select view type:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="tabListView">
<property name="text">
<string>Tab List</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="tabTreeView">
<property name="text">
<string>Tab Tree</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QCheckBox" name="replaceTabBar">
<property name="text">
<string>Use as replacement for main TabBar.</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,58 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#include "verticaltabswidget.h"
#include "tabtreeview.h"
#include "tabmodel.h"
#include "tabtreemodel.h"
#include "browserwindow.h"
#include <QVBoxLayout>
VerticalTabsWidget::VerticalTabsWidget(BrowserWindow *window)
: QWidget()
, m_window(window)
{
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setSpacing(0);
layout->setContentsMargins(0, 0, 0, 0);
m_view = new TabTreeView(this);
layout->addWidget(m_view);
}
void VerticalTabsWidget::setViewType(VerticalTabsPlugin::ViewType type)
{
switch (type) {
case VerticalTabsPlugin::TabListView:
m_view->setModel(m_window->tabModel());
m_view->setTabsInOrder(true);
break;
case VerticalTabsPlugin::TabTreeView:
delete m_treeModel;
m_treeModel = new TabTreeModel(this);
m_treeModel->setSourceModel(m_window->tabModel());
m_view->setModel(m_treeModel);
m_view->setTabsInOrder(false);
break;
default:
break;
};
}

View File

@ -0,0 +1,41 @@
/* ============================================================
* VerticalTabs plugin for Falkon
* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* ============================================================ */
#pragma once
#include <QWidget>
#include "verticaltabsplugin.h"
class BrowserWindow;
class TabTreeModel;
class TabTreeView;
class VerticalTabsWidget : public QWidget
{
Q_OBJECT
public:
explicit VerticalTabsWidget(BrowserWindow *window);
void setViewType(VerticalTabsPlugin::ViewType type);
private:
BrowserWindow *m_window;
TabTreeView *m_view;
TabTreeModel *m_treeModel = nullptr;
};