Merge pull request #573 from kiwix/454-moving-tabs

#454 Allow to move tabs
This commit is contained in:
Kelson 2021-02-24 13:02:00 +01:00 committed by GitHub
commit 0183274f8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 157 additions and 85 deletions

3
.gitignore vendored
View File

@ -30,3 +30,6 @@
*.exe
*.out
*.app
# QtCreator Environment Settings (not suitable for sharing)
kiwix-desktop.pro.user

View File

@ -250,15 +250,7 @@ void ContentManager::eraseBookFilesFromComputer(const QString dirPath, const QSt
void ContentManager::eraseBook(const QString& id)
{
auto tabBar = KiwixApp::instance()->getTabWidget();
int i = 1;
while (i < tabBar->count() - 1) {
WebView* webView = tabBar->widget(i)->getWebView();
if (webView->zimId() == id) {
tabBar->closeTab(i);
} else {
i++;
}
}
tabBar->closeTabsByZimId(id);
kiwix::Book book = mp_library->getBookById(id);
QString dirPath = QString::fromStdString(removeLastPathElement(book.getPath()));
QString filename = QString::fromStdString(getLastPathElement(book.getPath())) + "*";

View File

@ -6,6 +6,7 @@
class ContentManagerView : public QWebEngineView
{
Q_OBJECT
public:
ContentManagerView(QWidget *parent = Q_NULLPTR);
void registerObject(const QString &id, QObject *object);

View File

@ -180,7 +180,7 @@ void KiwixApp::openZimFile(const QString &zimfile)
void KiwixApp::printPage()
{
if(!mp_tabWidget->currentWidget())
if(!mp_tabWidget->currentZimView())
return;
QPrinter* printer = new QPrinter();
QPrintDialog printDialog(printer, mp_mainWindow);

View File

@ -155,8 +155,8 @@ void SearchBar::updateCompletion()
{
mp_typingTimer->stop();
clearSuggestions();
auto currentWidget = KiwixApp::instance()->getTabWidget()->currentWebView();
if (!currentWidget || currentWidget->url().isEmpty() || m_searchbarInput.isEmpty()) {
WebView* current = KiwixApp::instance()->getTabWidget()->currentWebView();
if (!current || current->url().isEmpty() || m_searchbarInput.isEmpty()) {
hideSuggestions();
return;
}

View File

@ -6,6 +6,7 @@
class SettingsManagerView : public QWebEngineView
{
Q_OBJECT
public:
SettingsManagerView(QWidget *parent = nullptr);
void registerObject(const QString &id, QObject *object);

View File

@ -13,10 +13,10 @@ void SuggestionListWorker::run()
QStringList suggestionList;
QVector<QUrl> urlList;
auto currentWidget = KiwixApp::instance()->getTabWidget()->currentWebView();
if(!currentWidget)
WebView *current = KiwixApp::instance()->getTabWidget()->currentWebView();
if(!current)
return;
auto qurl = currentWidget->url();
auto qurl = current->url();
auto currentZimId = qurl.host().split(".")[0];
auto reader = KiwixApp::instance()->getLibrary()->getReader(currentZimId);
QUrl url;

View File

@ -10,20 +10,19 @@
#include <QPainter>
#define QUITIFNULL(VIEW) if (nullptr==(VIEW)) { return; }
#define QUITIFNOTCURRENT(VIEW) if((VIEW)!=currentWidget()) {return;}
#define CURRENTIFNULL(VIEW) if(nullptr==VIEW) { VIEW = currentWidget();}
#define CURRENTIFNULL(VIEW) if(nullptr==VIEW) { VIEW = currentZimView();}
TabBar::TabBar(QWidget *parent) :
QTabBar(parent),
m_settingsIndex(-1)
QTabBar(parent)
{
QTabBar::setDrawBase(false);
setTabsClosable(true);
setElideMode(Qt::ElideNone);
setDocumentMode(true);
setFocusPolicy(Qt::NoFocus);
setMovable(true);
setIconSize(QSize(30, 30));
connect(this, &QTabBar::currentChanged, this, &TabBar::onCurrentChanged);
connect(this, &QTabBar::currentChanged, this, &TabBar::onCurrentChanged, Qt::QueuedConnection);
auto app = KiwixApp::instance();
connect(app->getAction(KiwixApp::NewTabAction), &QAction::triggered,
@ -38,7 +37,12 @@ TabBar::TabBar(QWidget *parent) :
connect(app->getAction(KiwixApp::CloseTabAction), &QAction::triggered,
this, [=]() {
auto index = this->tabAt(mapFromGlobal(QCursor::pos()));
if (index <= 0) {
if (index < 0)
return;
// library tab cannot be closed
QWidget *w = mp_stackedWidget->widget(index);
if (qobject_cast<ContentManagerView*>(w)) {
return;
}
this->closeTab(index);
@ -51,13 +55,14 @@ TabBar::TabBar(QWidget *parent) :
});
connect(app->getAction(KiwixApp::SettingAction), &QAction::triggered,
this, [=]() {
if (KiwixApp::instance()->getSettingsManager()->isSettingsViewdisplayed()) {
setCurrentIndex(m_settingsIndex);
return;
for (int i = 0 ; i < (mp_stackedWidget->count() - 1) ; i++) {
if (qobject_cast<SettingsManagerView*>(mp_stackedWidget->widget(i))) {
setCurrentIndex(i);
return;
}
}
auto index = currentIndex() + 1;
m_settingsIndex = index;
auto view = KiwixApp::instance()->getSettingsManager()->getView();
int index = currentIndex() + 1;
SettingsManagerView* view = KiwixApp::instance()->getSettingsManager()->getView();
mp_stackedWidget->insertWidget(index, view);
insertTab(index,QIcon(":/icons/settings.svg"), gt("settings"));
QToolButton *tb = new QToolButton(this);
@ -65,6 +70,10 @@ TabBar::TabBar(QWidget *parent) :
setTabButton(index, QTabBar::RightSide, tb);
setCurrentIndex(index);
});
// the slot relies the connection will be direct to reverting back the tab
connect(this, SIGNAL(tabMoved(int,int)),
this, SLOT(onTabMoved(int,int)), Qt::DirectConnection);
}
void TabBar::setStackedWidget(QStackedWidget *widget) {
@ -76,11 +85,10 @@ void TabBar::setStackedWidget(QStackedWidget *widget) {
void TabBar::setContentManagerView(ContentManagerView* view)
{
qInfo() << "add widget";
mp_contentManagerView = view;
mp_stackedWidget->addWidget(mp_contentManagerView);
mp_stackedWidget->addWidget(view);
mp_stackedWidget->show();
addTab(QIcon(":/icons/library-icon.svg"), "");
setTabButton(0, RightSide, nullptr);
int idx = addTab(QIcon(":/icons/library-icon.svg"), "");
setTabButton(idx, RightSide, nullptr);
}
void TabBar::setNewTabButton()
@ -88,19 +96,19 @@ void TabBar::setNewTabButton()
QToolButton *tb = new QToolButton();
tb->setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::NewTabAction));
tb->setIcon(QIcon(":/icons/new-tab-icon.svg"));
addTab("");
setTabEnabled(1, false);
setTabButton(1, QTabBar::LeftSide, tb);
tabButton(1, QTabBar::RightSide)->deleteLater();
setTabButton(1, QTabBar::RightSide, 0);
int idx = addTab("");
setTabEnabled(idx, false);
setTabButton(idx, QTabBar::LeftSide, tb);
tabButton(idx, QTabBar::RightSide)->deleteLater();
setTabButton(idx, QTabBar::RightSide, Q_NULLPTR);
}
ZimView* TabBar::createNewTab(bool setCurrent)
{
auto tab = new ZimView(this, this);
auto index = count() - 1;
int index = count() - 1; // the last tab is + button, insert before
mp_stackedWidget->insertWidget(index, tab);
insertTab(index, "");
index = insertTab(index, "");
QToolButton *tb = new QToolButton(this);
tb->setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::CloseTabAction));
setTabButton(index, QTabBar::RightSide, tb);
@ -162,34 +170,39 @@ void TabBar::setIconOf(const QIcon &icon, ZimView *tab)
QString TabBar::currentZimId()
{
if (!currentWidget())
return "";
return currentWebView()->zimId();
if (WebView *w = currentWebView())
return w->zimId();
return "";
}
QString TabBar::currentArticleUrl()
{
if(!currentWidget())
return "";
return currentWebView()->url().path();
if (WebView *w = currentWebView())
return w->url().path();
return "";
}
QString TabBar::currentArticleTitle()
{
if(!currentWidget())
return "";
return currentWebView()->title();
if (WebView *w = currentWebView())
return w->title();
return "";
}
QSize TabBar::tabSizeHint(int index) const {
if (index)
return QSize(205, 40);
return QSize(40, 40);
QSize TabBar::tabSizeHint(int index) const
{
QWidget *w = mp_stackedWidget->widget(index);
if (w && qobject_cast<ContentManagerView*>(w))
return QSize(40, 40); // the "Library" tab is only icon
return QSize(205, 40); // "Settings" and content tabs have text
}
void TabBar::openFindInPageBar()
{
currentWidget()->openFindInPageBar();
if (ZimView *zv = currentZimView())
zv->openFindInPageBar();
}
void TabBar::triggerWebPageAction(QWebEnginePage::WebAction action, ZimView *widget)
@ -200,23 +213,39 @@ void TabBar::triggerWebPageAction(QWebEnginePage::WebAction action, ZimView *wid
widget->getWebView()->setFocus();
}
void TabBar::closeTabsByZimId(const QString &id)
{
// the last tab is + button, skip it
for (int i = count() - 2 ; i >= 0 ; i--) {
auto *zv = qobject_cast<ZimView*>(mp_stackedWidget->widget(i));
if (!zv)
continue;
if (zv->getWebView()->zimId() == id) {
closeTab(i);
}
}
}
void TabBar::closeTab(int index)
{
setSelectionBehaviorOnRemove(index);
if (index == 0 || index == this->count() - 1)
// the last tab is + button, cannot be closed
if (index == this->count() - 1)
return;
setSelectionBehaviorOnRemove(index);
QWidget *view = mp_stackedWidget->widget(index);
// library tab cannot be closed
if (qobject_cast<ContentManagerView*>(view)) {
return;
if (index == m_settingsIndex) {
m_settingsIndex = -1;
}
if (index < m_settingsIndex) {
m_settingsIndex--;
}
auto webview = widget(index);
mp_stackedWidget->removeWidget(webview);
webview->setParent(nullptr);
mp_stackedWidget->removeWidget(view);
view->setParent(nullptr);
removeTab(index);
webview->close();
delete webview;
view->close();
view->deleteLater();
}
void TabBar::setSelectionBehaviorOnRemove(int index)
@ -232,14 +261,23 @@ void TabBar::onCurrentChanged(int index)
{
if (index == -1)
return;
if (index == m_settingsIndex) {
// if somehow the last tab (+ button) became active, switch to the previous
if (index >= (count() - 1)) {
setCurrentIndex(count() - 2);
return;
}
QWidget *w = mp_stackedWidget->widget(index);
if (qobject_cast<SettingsManagerView*>(w)) {
emit webActionEnabledChanged(QWebEnginePage::Back, false);
emit webActionEnabledChanged(QWebEnginePage::Forward, false);
emit libraryPageDisplayed(false);
KiwixApp::instance()->setSideBar(KiwixApp::NONE);
QTimer::singleShot(0, [=](){emit currentTitleChanged("");});
} else if (index) {
auto view = widget(index)->getWebView();
} else if (auto zv = qobject_cast<ZimView*>(w)) {
auto view = zv->getWebView();
emit webActionEnabledChanged(QWebEnginePage::Back, view->isWebActionEnabled(QWebEnginePage::Back));
emit webActionEnabledChanged(QWebEnginePage::Forward, view->isWebActionEnabled(QWebEnginePage::Forward));
emit libraryPageDisplayed(false);
@ -247,13 +285,18 @@ void TabBar::onCurrentChanged(int index)
KiwixApp::instance()->setSideBar(KiwixApp::NONE);
}
QTimer::singleShot(0, [=](){emit currentTitleChanged(view->title());});
} else {
} else if (qobject_cast<ContentManagerView*>(w)) {
emit webActionEnabledChanged(QWebEnginePage::Back, false);
emit webActionEnabledChanged(QWebEnginePage::Forward, false);
emit libraryPageDisplayed(true);
KiwixApp::instance()->setSideBar(KiwixApp::CONTENTMANAGER_BAR);
QTimer::singleShot(0, [=](){emit currentTitleChanged("");});
}
else {
Q_ASSERT(false);
// In the future, other types of tabs can be added.
// For example, About dialog, or Kiwix Server control panel.
}
}
void TabBar::fullScreenRequested(QWebEngineFullScreenRequest request)
@ -351,3 +394,29 @@ void TabBar::paintEvent(QPaintEvent *e)
p.fillRect(tail, br);
}
}
void TabBar::onTabMoved(int from, int to)
{
// avoid infinitive recursion
static bool reverting = false;
if (reverting)
return;
// on attempt to move the last tab (+ button) just move it back
// and the library should stick the first (zero) tab
int last = mp_stackedWidget->count();
if (
(from == last || to == last) ||
(from == 0 || to == 0)
) {
reverting = true;
moveTab(to, from);
reverting = false;
return;
}
// swap widgets
QWidget *w_from = mp_stackedWidget->widget(from);
mp_stackedWidget->removeWidget(w_from);
mp_stackedWidget->insertWidget(to, w_from);
}

View File

@ -23,17 +23,16 @@ public:
void setContentManagerView(ContentManagerView* view);
void setNewTabButton();
ZimView* createNewTab(bool setCurrent);
ZimView* widget(int index) { return (index != 0) ? static_cast<ZimView*>(mp_stackedWidget->widget(index)) : nullptr; }
WebView* currentWebView() { auto current = mp_stackedWidget->currentWidget();
if (mp_stackedWidget->currentIndex() == 0 ||
mp_stackedWidget->currentIndex() == m_settingsIndex) return nullptr;
return static_cast<ZimView*>(current)->getWebView();
}
ZimView* currentWidget() { auto current = mp_stackedWidget->currentWidget();
if (mp_stackedWidget->currentIndex() == 0 ||
mp_stackedWidget->currentIndex() == m_settingsIndex) return nullptr;
return static_cast<ZimView*>(current);
}
ZimView* currentZimView() {
return qobject_cast<ZimView*>(mp_stackedWidget->currentWidget());
}
WebView* currentWebView() {
if (ZimView *zv = currentZimView())
return zv->getWebView();
return nullptr;
}
void openUrl(const QUrl &url, bool newTab);
// Redirect call to sub-webView
@ -46,6 +45,7 @@ public:
QString currentArticleTitle();
virtual QSize tabSizeHint(int index) const;
void openFindInPageBar();
void closeTabsByZimId(const QString &id);
protected:
void mousePressEvent(QMouseEvent *event);
@ -63,13 +63,13 @@ public slots:
void fullScreenRequested(QWebEngineFullScreenRequest request);
private:
ContentManagerView* mp_contentManagerView;
QStackedWidget* mp_stackedWidget;
int m_settingsIndex;
QScopedPointer<FullScreenWindow> m_fullScreenWindow;
void setSelectionBehaviorOnRemove(int index);
private slots:
void onTabMoved(int from, int to);
};
#endif // TABWIDGET_H

View File

@ -47,7 +47,7 @@ ZimView::ZimView(TabBar *tabBar, QWidget *parent)
connect(mp_webView, &WebView::titleChanged, this,
[=](const QString& str) {
mp_tabBar->setTitleOf(str, this);
if (mp_tabBar->currentWidget() != this) {
if (mp_tabBar->currentZimView() != this) {
return;
}
emit mp_tabBar->currentTitleChanged(str);
@ -56,21 +56,21 @@ ZimView::ZimView(TabBar *tabBar, QWidget *parent)
[=](const QIcon& icon) { mp_tabBar->setIconOf(icon, this); });
connect(mp_webView, &WebView::zimIdChanged, this,
[=](const QString& zimId) {
if (mp_tabBar->currentWidget() != this) {
if (mp_tabBar->currentZimView() != this) {
return;
}
emit mp_tabBar->currentZimIdChanged(zimId);
});
connect(mp_webView->page()->action(QWebEnginePage::Back), &QAction::changed,
[=]() {
if (mp_tabBar->currentWidget() != this) {
if (mp_tabBar->currentZimView() != this) {
return;
}
emit mp_tabBar->webActionEnabledChanged(QWebEnginePage::Back, mp_webView->isWebActionEnabled(QWebEnginePage::Back));
});
connect(mp_webView->page()->action(QWebEnginePage::Forward), &QAction::changed,
[=]() {
if (mp_tabBar->currentWidget() != this) {
if (mp_tabBar->currentZimView() != this) {
return;
}
emit mp_tabBar->webActionEnabledChanged(QWebEnginePage::Forward, mp_webView->isWebActionEnabled(QWebEnginePage::Forward));
@ -87,7 +87,13 @@ ZimView::ZimView(TabBar *tabBar, QWidget *parent)
if (url.startsWith("zim://")) {
link = QUrl(url).path();
}
/* because we use QWebEnginePage::linkHovered signal,
* we can be sure the current tab is a web page
* (mp_tabBar->currentWebView() is not nullptr)
*/
auto height = mp_tabBar->currentWebView()->height() + 1;
auto pos = mp_tabBar->mapToGlobal(QPoint(-3, height));
QToolTip::showText(pos, link);
}