diff --git a/resources/css/style.css b/resources/css/style.css
index 05ed9f6..ae6593e 100644
--- a/resources/css/style.css
+++ b/resources/css/style.css
@@ -167,7 +167,9 @@ QTabBar {
}
QTabBar::tear {
- background-color: blue;
+ width: 0px;
+ height: 0px;
+ border: none;
}
QTabWidget::pane {
@@ -194,19 +196,33 @@ QTabBar::tab:first {
padding-left: 5px;
}
-QTabBar::tab:last QToolButton {
+#closeTabButton, #newTabButton {
font-size: 30px;
width: 30px; min-width: 30px; max-width: 30px;
height: 30px; min-height:30px; max-height:30px;
border-radius: 3px;
}
-QTabBar::tab:last QToolButton::hover {
+#nextTabButton, #prevTabButton, #newTabSideButton {
+ border: 1px solid #ccc;
+}
+
+#closeTabButton:hover, #newTabButton:hover,
+#nextTabButton:hover, #prevTabButton:hover,
+#newTabSideButton:hover {
border: 1px solid #3366CC;
background-color: #D9E9FF;
border-radius: 3px;
}
+QTabBar::scroller {
+ width: 0px;
+ height: 0px;
+
+ /* Last tab size is off by 1 if border not set. */
+ border: 1px solid transparent;
+}
+
/* -----------------------------------------
TabWidget
*/
diff --git a/resources/i18n/en.json b/resources/i18n/en.json
index e897268..51a4af5 100644
--- a/resources/i18n/en.json
+++ b/resources/i18n/en.json
@@ -165,5 +165,7 @@
"import-reading-list": "Import reading list",
"import-reading-list-error": "An error has occured during import of the reading list.",
"disable-sandbox": "Application was launched from a network drive. This is known to cause compatibility issues due to the sandbox. Do you want to take the risks and disable it?",
- "save-page-as": "Save As..."
+ "save-page-as": "Save As...",
+ "scroll-next-tab": "Scroll to next tab",
+ "scroll-previous-tab": "Scroll to previous tab"
}
diff --git a/resources/i18n/qqq.json b/resources/i18n/qqq.json
index 99b3f9b..2e133a2 100644
--- a/resources/i18n/qqq.json
+++ b/resources/i18n/qqq.json
@@ -172,5 +172,7 @@
"export-reading-list-error": "Error description text for when exporting the reading list to a file failed.",
"import-reading-list": "Represents the action of importing a reading list from a file.",
"import-reading-list-error": "Error description text for when importing a reading list from a file failed.",
- "save-page-as": "Represents the action of saving the current tab content to a file chosen by the user."
+ "save-page-as": "Represents the action of saving the current tab content to a file chosen by the user.",
+ "scroll-next-tab": "Represents the action of scrolling to the next tab of the current tab which toward the end of the tab bar.",
+ "scroll-previous-tab": "Represents the action of scrolling to the previous tab of the current tab which toward the start of the tab bar."
}
diff --git a/resources/icons/caret-left-solid.svg b/resources/icons/caret-left-solid.svg
new file mode 100644
index 0000000..817bbc4
--- /dev/null
+++ b/resources/icons/caret-left-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/resources/kiwix.qrc b/resources/kiwix.qrc
index fd08d1b..988ce71 100644
--- a/resources/kiwix.qrc
+++ b/resources/kiwix.qrc
@@ -65,5 +65,6 @@
icons/xmark-solid.svg
icons/home-button.svg
icons/more-vertical.svg
+ icons/caret-left-solid.svg
diff --git a/src/kiwixapp.cpp b/src/kiwixapp.cpp
index 58fe0e5..20b9b9c 100644
--- a/src/kiwixapp.cpp
+++ b/src/kiwixapp.cpp
@@ -385,16 +385,16 @@ void KiwixApp::createActions()
if (QGuiApplication::isLeftToRight()) {
CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-left", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Left));
+ CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-right", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Right));
+ CREATE_ACTION_ICON(ScrollNextTabAction, "caret-right-solid", gt("scroll-next-tab"));
+ CREATE_ACTION_ICON(ScrollPreviousTabAction, "caret-left-solid", gt("scroll-previous-tab"));
} else {
CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-right", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Right));
+ CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-left", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Left));
+ CREATE_ACTION_ICON(ScrollNextTabAction, "caret-left-solid", gt("scroll-next-tab"));
+ CREATE_ACTION_ICON(ScrollPreviousTabAction, "caret-right-solid", gt("scroll-previous-tab"));
}
DISABLE_ACTION(HistoryBackAction);
-
- if (QGuiApplication::isLeftToRight()) {
- CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-right", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Right));
- } else {
- CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-left", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Left));
- }
DISABLE_ACTION(HistoryForwardAction);
CREATE_ACTION_ICON_SHORTCUT(PrintAction, "print", gt("print"), QKeySequence::Print);
diff --git a/src/kiwixapp.h b/src/kiwixapp.h
index c9ed3f7..5454150 100644
--- a/src/kiwixapp.h
+++ b/src/kiwixapp.h
@@ -64,6 +64,8 @@ public:
ExitAction,
ExportReadingListAction,
ImportReadingListAction,
+ ScrollPreviousTabAction,
+ ScrollNextTabAction,
MAX_ACTION
};
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index a34b872..6b2feb8 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -27,6 +27,18 @@ MainWindow::MainWindow(QWidget *parent) :
addAction(app->getAction(KiwixApp::NextTabAction));
addAction(app->getAction(KiwixApp::PreviousTabAction));
+ mp_ui->newTabSideButton->setDefaultAction(app->getAction(KiwixApp::NewTabAction));
+ mp_ui->nextTabButton->setDefaultAction(app->getAction(KiwixApp::ScrollNextTabAction));
+ mp_ui->prevTabButton->setDefaultAction(app->getAction(KiwixApp::ScrollPreviousTabAction));
+
+ connect(mp_ui->tabBar, &TabBar::sizeChanged, this, &MainWindow::updateTabButtons);
+ connect(mp_ui->tabBar, &QTabBar::currentChanged, this, &MainWindow::updateTabButtons);
+ connect(mp_ui->tabBar, &TabBar::tabRemovedSignal, this, &MainWindow::updateTabButtons);
+ connect(mp_ui->tabBar, &TabBar::tabInsertedSignal, this, &MainWindow::updateTabButtons);
+
+ connect(mp_ui->nextTabButton, &QToolButton::triggered, mp_ui->tabBar, &TabBar::scrollNextTab);
+ connect(mp_ui->prevTabButton, &QToolButton::triggered, mp_ui->tabBar, &TabBar::scrollPreviousTab);
+
connect(app->getAction(KiwixApp::ExitAction), &QAction::triggered,
this, &QMainWindow::close);
connect(app->getAction(KiwixApp::ToggleFullscreenAction), &QAction::triggered,
@@ -102,6 +114,32 @@ void MainWindow::showTabAndTop() {
getTopWidget()->show();
}
+void MainWindow::updateTabButtons()
+{
+ auto tabBar = getTabBar();
+ QRect tabBarRect = getTabBar()->rect();
+ QRect newTabButtonRect = tabBar->tabRect(tabBar->count() - 1);
+
+ /* Decision is made at half way of the new button tab for smoothness */
+ newTabButtonRect.setWidth(newTabButtonRect.width() / 2);
+ bool newTabButtonVisible = tabBarRect.contains(newTabButtonRect);
+ mp_ui->newTabSideButton->setHidden(newTabButtonVisible);
+
+ int lastTabIndex = tabBar->realTabCount() - 1;
+ QRect firstTabRect = tabBar->tabRect(0);
+ QRect lastTabRect = tabBar->tabRect(lastTabIndex);
+
+ bool firstVisible = tabBarRect.contains(firstTabRect);
+ bool lastVisible = tabBarRect.contains(lastTabRect);
+ bool noOverFlow = firstVisible && lastVisible;
+
+ mp_ui->prevTabButton->setHidden(noOverFlow);
+ mp_ui->nextTabButton->setHidden(noOverFlow);
+
+ mp_ui->prevTabButton->setDisabled(tabBar->currentIndex() == 0);
+ mp_ui->nextTabButton->setDisabled(tabBar->currentIndex() == lastTabIndex);
+}
+
bool MainWindow::eventFilter(QObject* /*object*/, QEvent* event)
{
if (event->type() == QEvent::MouseMove && isFullScreen())
@@ -129,6 +167,7 @@ void MainWindow::resizeEvent(QResizeEvent *event)
{
QMainWindow::resizeEvent(event);
KiwixApp::instance()->getContentManager()->getView()->updateSizeHint();
+ updateTabButtons();
}
void MainWindow::readingListToggled(bool state)
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 2dbb882..a97bef1 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -37,6 +37,7 @@ private slots:
void readingListToggled(bool state);
void hideTabAndTop();
void showTabAndTop();
+ void updateTabButtons();
private:
Ui::MainWindow *mp_ui;
diff --git a/src/tabbar.cpp b/src/tabbar.cpp
index a3b2d78..71b5c53 100644
--- a/src/tabbar.cpp
+++ b/src/tabbar.cpp
@@ -104,6 +104,7 @@ void TabBar::setContentManagerView(ContentManagerView* view)
void TabBar::setNewTabButton(QAction* newTabAction)
{
QToolButton *tb = new QToolButton();
+ tb->setObjectName("newTabButton");
tb->setDefaultAction(newTabAction);
tb->setIcon(QIcon(":/icons/new-tab-icon.svg"));
int idx = addTab("");
@@ -133,11 +134,24 @@ void TabBar::moveToPreviousTab()
setCurrentIndex(index <= 0 ? realTabCount() - 1 : index - 1);
}
+void TabBar::scrollNextTab()
+{
+ const int index = currentIndex();
+ setCurrentIndex(index == realTabCount() - 1 ? index : index + 1);
+}
+
+void TabBar::scrollPreviousTab()
+{
+ const int index = currentIndex();
+ setCurrentIndex(index <= 0 ? index : index - 1);
+}
+
void TabBar::setCloseTabButton(int index)
{
Q_ASSERT(index > 0 && index < realTabCount());
QToolButton *tb = new QToolButton(this);
+ tb->setObjectName("closeTabButton");
QAction *a = new QAction(QIcon(":/icons/close.svg"), gt("close-tab"), tb);
a->setToolTip(getAction(KiwixApp::CloseCurrentTabAction)->toolTip());
tb->setDefaultAction(a);
@@ -238,7 +252,7 @@ QSize TabBar::tabSizeHint(int index) const
{
QWidget *w = mp_stackedWidget->widget(index);
- if (w && qobject_cast(w))
+ if ((w && qobject_cast(w)) || index >= count() - 1)
return QSize(40, 40); // the "Library" tab is only icon
return QSize(205, 40); // "Settings" and content tabs have text
@@ -491,6 +505,24 @@ void TabBar::paintEvent(QPaintEvent *e)
}
}
+void TabBar::tabRemoved(int index)
+{
+ QTabBar::tabRemoved(index);
+ emit tabRemovedSignal(index);
+}
+
+void TabBar::tabInserted(int index)
+{
+ QTabBar::tabInserted(index);
+ emit tabInsertedSignal(index);
+}
+
+void TabBar::resizeEvent(QResizeEvent *event)
+{
+ QTabBar::resizeEvent(event);
+ emit sizeChanged();
+}
+
void TabBar::onTabMoved(int from, int to)
{
// avoid infinitive recursion
diff --git a/src/tabbar.h b/src/tabbar.h
index 9310756..6055734 100644
--- a/src/tabbar.h
+++ b/src/tabbar.h
@@ -52,14 +52,24 @@ public:
QStringList getTabUrls() const;
QStringList getTabZimIds() const;
+ // The "+" (new tab) button is implemented as a tab (that is always placed at the end).
+ // This function returns the count of real tabs.
+ int realTabCount() const;
+
protected:
void mousePressEvent(QMouseEvent *event);
void paintEvent(QPaintEvent *);
+ void tabRemoved(int index) override;
+ void tabInserted(int index) override;
+ void resizeEvent(QResizeEvent *) override;
signals:
void webActionEnabledChanged(QWebEnginePage::WebAction action, bool enabled);
void tabDisplayed(TabType tabType);
void currentTitleChanged(const QString& title);
+ void tabRemovedSignal(int index);
+ void tabInsertedSignal(int index);
+ void sizeChanged();
public slots:
void closeTab(int index);
@@ -69,6 +79,8 @@ public slots:
void on_webview_titleChanged(const QString& title);
void moveToNextTab();
void moveToPreviousTab();
+ void scrollNextTab();
+ void scrollPreviousTab();
private:
void setCloseTabButton(int index);
@@ -77,10 +89,6 @@ private:
QStackedWidget* mp_stackedWidget;
QScopedPointer m_fullScreenWindow;
- // The "+" (new tab) button is implemented as a tab (that is always placed at the end).
- // This function returns the count of real tabs.
- int realTabCount() const;
-
private slots:
void onTabMoved(int from, int to);
void onCurrentChanged(int index);
diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui
index 1c96224..dd2749e 100644
--- a/ui/mainwindow.ui
+++ b/ui/mainwindow.ui
@@ -6,7 +6,7 @@
0
0
- 1024
+ 968
576
@@ -40,50 +40,122 @@
0
-
-
-
- -
-
+
0
-
-
-
- true
-
-
-
- 0
- 0
-
-
-
- QFrame::NoFrame
-
-
- QFrame::Plain
-
-
+
+
0
-
-
-
- 0
- 0
-
-
-
-
-
+
-
+
+
+
+ 40
+ 40
+
+
+
+
+
+
+
+ 24
+ 24
+
+
+
+
+ -
+
+
+ -
+
+
+
+ 40
+ 40
+
+
+
+
+
+
+
+ :/icons/new-tab-icon.svg:/icons/new-tab-icon.svg
+
+
+
+ 16
+ 16
+
+
+
+
+ -
+
+
+
+ 40
+ 40
+
+
+
+
+
+
+
+ 24
+ 24
+
+
+
+
+
-
-
-
- background-color: rgb(255, 255, 255);
-
-
+
+
-
+
+
+ true
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ -
+
+
+ background-color: rgb(255, 255, 255);
+
+
+
+
@@ -130,6 +202,8 @@
1
-
+
+
+