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 - + + +