From 95ccd7602c29752b87538b6d3b3aad2ca55f742b Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Mon, 28 Oct 2024 18:16:02 -0400 Subject: [PATCH 01/11] Introduce MultiZim Search Button User can search zim other than the current tab. --- kiwix-desktop.pro | 2 + src/multizimbutton.cpp | 75 ++++++++++++++++++++++++++++++++++++ src/multizimbutton.h | 24 ++++++++++++ src/searchbar.cpp | 9 ++++- src/searchbar.h | 3 ++ src/suggestionlistworker.cpp | 12 +++--- 6 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 src/multizimbutton.cpp create mode 100644 src/multizimbutton.h diff --git a/kiwix-desktop.pro b/kiwix-desktop.pro index 9fb0c123..8d370af0 100644 --- a/kiwix-desktop.pro +++ b/kiwix-desktop.pro @@ -91,6 +91,7 @@ SOURCES += \ src/fullscreenwindow.cpp \ src/fullscreennotification.cpp \ src/zimview.cpp \ + src/multizimbutton.cpp \ HEADERS += \ src/choiceitem.h \ @@ -143,6 +144,7 @@ HEADERS += \ src/zimview.h \ src/portutils.h \ src/css_constants.h \ + src/multizimbutton.h \ FORMS += \ src/choiceitem.ui \ diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp new file mode 100644 index 00000000..10850e57 --- /dev/null +++ b/src/multizimbutton.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include "kiwixapp.h" +#include "multizimbutton.h" + +MultiZimButton::MultiZimButton(QWidget *parent) : + QToolButton(parent), + mp_buttonList(new QListWidget), + mp_radioButtonGroup(new QButtonGroup(this)) +{ + setMenu(new QMenu(this)); + setPopupMode(QToolButton::InstantPopup); + + const auto popupAction = new QWidgetAction(menu()); + popupAction->setDefaultWidget(mp_buttonList); + menu()->addAction(popupAction); +} + +void MultiZimButton::updateDisplay() +{ + mp_buttonList->clear(); + for (const auto& button : mp_radioButtonGroup->buttons()) + mp_radioButtonGroup->removeButton(button); + + const auto library = KiwixApp::instance()->getLibrary(); + for (const auto& bookId : library->getBookIds()) + { + try + { + library->getArchive(bookId); + } catch (...) { continue; } + + const QString bookTitle = QString::fromStdString(library->getBookById(bookId).getTitle()); + const QIcon zimIcon = library->getBookIcon(bookId); + + const auto item = new QListWidgetItem(); + item->setData(Qt::UserRole, bookId); + item->setData(Qt::DisplayRole, bookTitle); + + const auto radioBt = new QRadioButton(bookTitle); + radioBt->setIcon(zimIcon); + mp_radioButtonGroup->addButton(radioBt); + + mp_buttonList->addItem(item); + mp_buttonList->setItemWidget(item, radioBt); + } + + mp_buttonList->sortItems(); + + /* Display should not be used other than for sorting. */ + for (int i = 0; i < mp_buttonList->count(); i++) + mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant()); + + setDisabled(mp_buttonList->model()->rowCount() == 0); + + const auto firstWidget = mp_buttonList->itemWidget(mp_buttonList->item(0)); + if (const auto firstBt = qobject_cast(firstWidget)) + firstBt->setChecked(true); +} + +QStringList MultiZimButton::getZimIds() const +{ + QStringList idList; + for (int row = 0; row < mp_buttonList->count(); row++) + { + const auto listItem = mp_buttonList->item(row); + const auto radioBt = qobject_cast(mp_buttonList->itemWidget(listItem)); + if (radioBt && radioBt->isChecked()) + idList.append(listItem->data(Qt::UserRole).toString()); + } + return idList; +} diff --git a/src/multizimbutton.h b/src/multizimbutton.h new file mode 100644 index 00000000..e21b4235 --- /dev/null +++ b/src/multizimbutton.h @@ -0,0 +1,24 @@ +#ifndef MULTIZIMBUTTON_H +#define MULTIZIMBUTTON_H + +#include + +class QListWidget; +class QButtonGroup; + +class MultiZimButton : public QToolButton { + Q_OBJECT + +public: + explicit MultiZimButton(QWidget *parent = nullptr); + +public slots: + void updateDisplay(); + QStringList getZimIds() const; + +private: + QListWidget* mp_buttonList; + QButtonGroup* mp_radioButtonGroup; +}; + +#endif // MULTIZIMBUTTON_H diff --git a/src/searchbar.cpp b/src/searchbar.cpp index 253dcc88..792dce1f 100644 --- a/src/searchbar.cpp +++ b/src/searchbar.cpp @@ -414,7 +414,8 @@ QRect SearchBarLineEdit::getCompleterRect() const SearchBar::SearchBar(QWidget *parent) : QToolBar(parent), m_searchBarLineEdit(this), - m_bookmarkButton(this) + m_bookmarkButton(this), + m_multiZimButton(this) { QLabel* searchIconLabel = new QLabel; searchIconLabel->setObjectName("searchIcon"); @@ -425,9 +426,15 @@ SearchBar::SearchBar(QWidget *parent) : addWidget(searchIconLabel); addWidget(&m_searchBarLineEdit); addWidget(&m_bookmarkButton); + addWidget(&m_multiZimButton); connect(this, &SearchBar::currentTitleChanged, &m_searchBarLineEdit, &SearchBarLineEdit::on_currentTitleChanged); connect(this, &SearchBar::currentTitleChanged, &m_bookmarkButton, &BookmarkButton::update_display); + connect(KiwixApp::instance()->getContentManager(), + &ContentManager::booksChanged, &m_multiZimButton, + &MultiZimButton::updateDisplay); + connect(this, &SearchBar::currentTitleChanged, &m_multiZimButton, + &MultiZimButton::updateDisplay); } diff --git a/src/searchbar.h b/src/searchbar.h index 8b6b4e0b..1a9f6546 100644 --- a/src/searchbar.h +++ b/src/searchbar.h @@ -11,6 +11,7 @@ #include #include #include "suggestionlistmodel.h" +#include "multizimbutton.h" class QTreeView; @@ -78,6 +79,7 @@ class SearchBar : public QToolBar { public: SearchBar(QWidget *parent = nullptr); SearchBarLineEdit& getLineEdit() { return m_searchBarLineEdit; }; + MultiZimButton& getMultiZimButton() { return m_multiZimButton; }; signals: void currentTitleChanged(const QString &title); @@ -85,5 +87,6 @@ class SearchBar : public QToolBar { private: SearchBarLineEdit m_searchBarLineEdit; BookmarkButton m_bookmarkButton; + MultiZimButton m_multiZimButton; }; #endif // SEARCHBAR_H diff --git a/src/suggestionlistworker.cpp b/src/suggestionlistworker.cpp index 9230ef0a..5f328b04 100644 --- a/src/suggestionlistworker.cpp +++ b/src/suggestionlistworker.cpp @@ -14,13 +14,13 @@ void SuggestionListWorker::run() { QList suggestionList; - WebView *current = KiwixApp::instance()->getTabWidget()->currentWebView(); - if(!current) - return; - auto qurl = current->url(); - auto currentZimId = qurl.host().split(".")[0]; + const auto app = KiwixApp::instance(); + const auto selectedIdList = app->getSearchBar().getMultiZimButton().getZimIds(); + + /* TODO: re-implement this after introducing the actual Multi-Zim. */ + const auto currentZimId = selectedIdList[0]; try { - auto archive = KiwixApp::instance()->getLibrary()->getArchive(currentZimId); + const auto archive = app->getLibrary()->getArchive(currentZimId); QUrl url; url.setScheme("zim"); url.setHost(currentZimId + ".zim"); From e9c7421fc1fab90e4d4952c346ffbe657b953ce5 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Thu, 31 Oct 2024 21:39:46 -0400 Subject: [PATCH 02/11] Introduce KiwixApp::OpenMultiZimAction Shortcut and Icon for MultiZim. Since window is popup, toggle is not possible. --- resources/i18n/en.json | 3 ++- resources/i18n/qqq.json | 3 ++- src/kiwixapp.cpp | 2 ++ src/kiwixapp.h | 1 + src/mainmenu.cpp | 1 + src/multizimbutton.cpp | 2 ++ 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 9dfc1d69..b884f129 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -172,5 +172,6 @@ "portable-disabled-tooltip": "Function disabled in portable mode", "scroll-next-tab": "Scroll to next tab", "scroll-previous-tab": "Scroll to previous tab", - "kiwix-search": "Kiwix search" + "kiwix-search": "Kiwix search", + "search-options": "Search Options" } diff --git a/resources/i18n/qqq.json b/resources/i18n/qqq.json index 55846a1b..2fdc8c7b 100644 --- a/resources/i18n/qqq.json +++ b/resources/i18n/qqq.json @@ -180,5 +180,6 @@ "portable-disabled-tooltip": "Tooltip used to explain disabled components in the portable version.", "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.", - "kiwix-search": "Title text for a list of search results, which notes to the user those are from Kiwix's Search Engine" + "kiwix-search": "Title text for a list of search results, which notes to the user those are from Kiwix's Search Engine", + "search-options": "The term for a collection of additional search filtering options to help users narrow down search results." } diff --git a/src/kiwixapp.cpp b/src/kiwixapp.cpp index f9be1da4..ed29b526 100644 --- a/src/kiwixapp.cpp +++ b/src/kiwixapp.cpp @@ -438,6 +438,8 @@ void KiwixApp::createActions() CREATE_ACTION_SHORTCUT(ToggleTOCAction, gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_1)); HIDE_ACTION(ToggleTOCAction); + CREATE_ACTION_ICON_SHORTCUT(OpenMultiZimAction, "filter", gt("search-options"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L)); + CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL | Qt::Key_B)); CREATE_ACTION(ExportReadingListAction, gt("export-reading-list")); diff --git a/src/kiwixapp.h b/src/kiwixapp.h index fa422fc2..d8524c02 100644 --- a/src/kiwixapp.h +++ b/src/kiwixapp.h @@ -39,6 +39,7 @@ class KiwixApp : public QtSingleApplication BrowseLibraryAction, OpenFileAction, OpenRecentAction, + OpenMultiZimAction, SavePageAsAction, SearchArticleAction, SearchLibraryAction, diff --git a/src/mainmenu.cpp b/src/mainmenu.cpp index 07666716..1d550914 100644 --- a/src/mainmenu.cpp +++ b/src/mainmenu.cpp @@ -35,6 +35,7 @@ MainMenu::MainMenu(QWidget *parent) : m_editMenu.ADD_ACTION(SearchLibraryAction); m_editMenu.ADD_ACTION(FindInPageAction); m_editMenu.ADD_ACTION(ToggleAddBookmarkAction); + m_editMenu.ADD_ACTION(OpenMultiZimAction); addMenu(&m_editMenu); m_viewMenu.setTitle(gt("view")); diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index 10850e57..7341670c 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -13,6 +13,8 @@ MultiZimButton::MultiZimButton(QWidget *parent) : { setMenu(new QMenu(this)); setPopupMode(QToolButton::InstantPopup); + setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::OpenMultiZimAction)); + connect(this, &QToolButton::triggered, this, &MultiZimButton::showMenu); const auto popupAction = new QWidgetAction(menu()); popupAction->setDefaultWidget(mp_buttonList); From e489712787f96e5b67fbf0db9228c844fc8b4366 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Mon, 28 Oct 2024 22:50:18 -0400 Subject: [PATCH 03/11] Introduce ZimItemWidget to Delegate Item Display Decouple complex item creation from button. Used later for styling. --- src/multizimbutton.cpp | 53 ++++++++++++++++++++++++++++++++---------- src/multizimbutton.h | 20 ++++++++++++++++ 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index 7341670c..f70147e3 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -42,12 +42,8 @@ void MultiZimButton::updateDisplay() item->setData(Qt::UserRole, bookId); item->setData(Qt::DisplayRole, bookTitle); - const auto radioBt = new QRadioButton(bookTitle); - radioBt->setIcon(zimIcon); - mp_radioButtonGroup->addButton(radioBt); - mp_buttonList->addItem(item); - mp_buttonList->setItemWidget(item, radioBt); + setItemZimWidget(item, bookTitle, zimIcon); } mp_buttonList->sortItems(); @@ -58,9 +54,8 @@ void MultiZimButton::updateDisplay() setDisabled(mp_buttonList->model()->rowCount() == 0); - const auto firstWidget = mp_buttonList->itemWidget(mp_buttonList->item(0)); - if (const auto firstBt = qobject_cast(firstWidget)) - firstBt->setChecked(true); + if (const auto firstWidget = getZimWidget(0)) + firstWidget->getRadioButton()->setChecked(true); } QStringList MultiZimButton::getZimIds() const @@ -68,10 +63,44 @@ QStringList MultiZimButton::getZimIds() const QStringList idList; for (int row = 0; row < mp_buttonList->count(); row++) { - const auto listItem = mp_buttonList->item(row); - const auto radioBt = qobject_cast(mp_buttonList->itemWidget(listItem)); - if (radioBt && radioBt->isChecked()) - idList.append(listItem->data(Qt::UserRole).toString()); + const auto widget = getZimWidget(row); + if (widget && widget->getRadioButton()->isChecked()) + idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString()); } return idList; } + +ZimItemWidget *MultiZimButton::getZimWidget(int row) const +{ + const auto widget = mp_buttonList->itemWidget(mp_buttonList->item(row)); + return qobject_cast(widget); +} + +void MultiZimButton::setItemZimWidget(QListWidgetItem *item, + const QString &title, const QIcon &icon) +{ + const auto zimWidget = new ZimItemWidget(title, icon); + mp_radioButtonGroup->addButton(zimWidget->getRadioButton()); + mp_buttonList->setItemWidget(item, zimWidget); +} + +ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) : + QWidget(parent), + textLabel(new QLabel(this)), + iconLabel(new QLabel(this)), + radioBt(new QRadioButton(this)) +{ + setLayout(new QHBoxLayout); + layout()->setSpacing(0); + layout()->setContentsMargins(0, 0, 0, 0); + + textLabel->setText(text); + + /* TODO: change temporary values once size is defined laters */ + const QSize iconSize = QSize(24, 24); + iconLabel->setPixmap(icon.pixmap(iconSize)); + + layout()->addWidget(iconLabel); + layout()->addWidget(textLabel); + layout()->addWidget(radioBt); +} diff --git a/src/multizimbutton.h b/src/multizimbutton.h index e21b4235..0f20ffeb 100644 --- a/src/multizimbutton.h +++ b/src/multizimbutton.h @@ -5,6 +5,23 @@ class QListWidget; class QButtonGroup; +class QListWidgetItem; +class QRadioButton; +class QLabel; + +class ZimItemWidget : public QWidget { + Q_OBJECT + +public: + ZimItemWidget(QString text, QIcon icon, QWidget *parent = nullptr); + + QRadioButton* getRadioButton() const { return radioBt; } + +private: + QLabel* textLabel; + QLabel* iconLabel; + QRadioButton* radioBt; +}; class MultiZimButton : public QToolButton { Q_OBJECT @@ -19,6 +36,9 @@ public slots: private: QListWidget* mp_buttonList; QButtonGroup* mp_radioButtonGroup; + + ZimItemWidget* getZimWidget(int row) const; + void setItemZimWidget(QListWidgetItem* item, const QString& title, const QIcon& icon); }; #endif // MULTIZIMBUTTON_H From 19c7acc5f7097cba2a916f93078a8161beb1df62 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Mon, 28 Oct 2024 22:57:19 -0400 Subject: [PATCH 04/11] LineEdit Should PopupFocus Missing this reason cause completer to do nothing --- src/searchbar.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/searchbar.cpp b/src/searchbar.cpp index 792dce1f..b80bea15 100644 --- a/src/searchbar.cpp +++ b/src/searchbar.cpp @@ -204,7 +204,8 @@ void SearchBarLineEdit::focusInEvent( QFocusEvent* event) } if (event->reason() == Qt::ActiveWindowFocusReason || event->reason() == Qt::MouseFocusReason || - event->reason() == Qt::ShortcutFocusReason) { + event->reason() == Qt::ShortcutFocusReason || + event->reason() == Qt::PopupFocusReason) { connect(&m_completer, QOverload::of(&QCompleter::activated), this, &QLineEdit::setText,Qt::UniqueConnection); From f5bef3bf45f6451b3a976e6be3b2e7905de9acab Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 30 Oct 2024 00:34:53 -0400 Subject: [PATCH 05/11] MultiZim Auto-Select Current Zim --- src/multizimbutton.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index f70147e3..be7761be 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -28,6 +28,9 @@ void MultiZimButton::updateDisplay() mp_radioButtonGroup->removeButton(button); const auto library = KiwixApp::instance()->getLibrary(); + const auto view = KiwixApp::instance()->getTabWidget()->currentWebView(); + QListWidgetItem* currentItem = nullptr; + QIcon currentIcon; for (const auto& bookId : library->getBookIds()) { try @@ -42,11 +45,25 @@ void MultiZimButton::updateDisplay() item->setData(Qt::UserRole, bookId); item->setData(Qt::DisplayRole, bookTitle); + if (view && view->zimId() == bookId) + { + currentItem = item; + currentIcon = zimIcon; + continue; + } + mp_buttonList->addItem(item); setItemZimWidget(item, bookTitle, zimIcon); } mp_buttonList->sortItems(); + if (currentItem) + { + mp_buttonList->insertItem(0, currentItem); + + const auto title = currentItem->data(Qt::DisplayRole).toString(); + setItemZimWidget(currentItem, "*" + title, currentIcon); + } /* Display should not be used other than for sorting. */ for (int i = 0; i < mp_buttonList->count(); i++) @@ -54,6 +71,7 @@ void MultiZimButton::updateDisplay() setDisabled(mp_buttonList->model()->rowCount() == 0); + mp_buttonList->scrollToTop(); if (const auto firstWidget = getZimWidget(0)) firstWidget->getRadioButton()->setChecked(true); } From 85d2790b7548b6475949c1e3a6975cd6c89d8ec3 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 30 Oct 2024 01:22:44 -0400 Subject: [PATCH 06/11] Check MultiZim Item When Clicking Anywhere Previously only checks when clicking Radiobutton --- src/multizimbutton.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index be7761be..9d7c5e5e 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -19,6 +19,11 @@ MultiZimButton::MultiZimButton(QWidget *parent) : const auto popupAction = new QWidgetAction(menu()); popupAction->setDefaultWidget(mp_buttonList); menu()->addAction(popupAction); + + connect(mp_buttonList, &QListWidget::currentRowChanged, this, [=](int row){ + if (const auto widget = getZimWidget(row)) + widget->getRadioButton()->setChecked(true); + }); } void MultiZimButton::updateDisplay() @@ -72,8 +77,7 @@ void MultiZimButton::updateDisplay() setDisabled(mp_buttonList->model()->rowCount() == 0); mp_buttonList->scrollToTop(); - if (const auto firstWidget = getZimWidget(0)) - firstWidget->getRadioButton()->setChecked(true); + mp_buttonList->setCurrentRow(0); } QStringList MultiZimButton::getZimIds() const From c5f2f766b0176b190b807b2b39bd82eafe2646ba Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 30 Oct 2024 01:18:13 -0400 Subject: [PATCH 07/11] Proper MultiZim List Sizing Set height&padding for list&items and stretch to menu width. --- resources/css/style.css | 12 ++++++++++++ src/css_constants.h | 12 ++++++++++++ src/multizimbutton.cpp | 13 +++++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/resources/css/style.css b/resources/css/style.css index ee91655e..b7e21d67 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -75,6 +75,18 @@ SearchBar > QToolButton:hover { border-radius: 3px; } +MultiZimButton QListWidget { + border: 0px; + outline: 0px; + padding: 5px 0px; /* XXX: duplicated in css_constants.h */ + background-color: white; +} + +ZimItemWidget QLabel { + font-size: 16px; + line-height: 24px; /* XXX: duplicated in css_constants.h */ +} + TopWidget QToolButton:pressed, TopWidget QToolButton::hover { border: 1px solid #3366CC; diff --git a/src/css_constants.h b/src/css_constants.h index dba10b0b..2df0acf4 100644 --- a/src/css_constants.h +++ b/src/css_constants.h @@ -28,6 +28,18 @@ namespace SearchBar{ const int border = 1; } +namespace MultiZimButton { +namespace QListWidget { + const int paddingVertical = 5; +} +} + +namespace ZimItemWidget { +namespace QLabel { + const int lineHeight = 24; +} +} + namespace TopWidget { namespace QToolButton { namespace backButton { diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index 9d7c5e5e..869c3820 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -5,6 +5,7 @@ #include #include "kiwixapp.h" #include "multizimbutton.h" +#include "css_constants.h" MultiZimButton::MultiZimButton(QWidget *parent) : QToolButton(parent), @@ -36,6 +37,8 @@ void MultiZimButton::updateDisplay() const auto view = KiwixApp::instance()->getTabWidget()->currentWebView(); QListWidgetItem* currentItem = nullptr; QIcon currentIcon; + const int paddingTopBot = CSS::MultiZimButton::QListWidget::paddingVertical * 2; + const int itemHeight = paddingTopBot + CSS::ZimItemWidget::QLabel::lineHeight; for (const auto& bookId : library->getBookIds()) { try @@ -49,6 +52,7 @@ void MultiZimButton::updateDisplay() const auto item = new QListWidgetItem(); item->setData(Qt::UserRole, bookId); item->setData(Qt::DisplayRole, bookTitle); + item->setSizeHint(QSize(0, itemHeight)); if (view && view->zimId() == bookId) { @@ -78,6 +82,11 @@ void MultiZimButton::updateDisplay() mp_buttonList->scrollToTop(); mp_buttonList->setCurrentRow(0); + + /* We set a maximum display height for list. Respect padding. */ + const int listHeight = itemHeight * std::min(7, mp_buttonList->count()); + mp_buttonList->setFixedHeight(listHeight + paddingTopBot); + mp_buttonList->setFixedWidth(menu()->width()); } QStringList MultiZimButton::getZimIds() const @@ -118,8 +127,8 @@ ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) : textLabel->setText(text); - /* TODO: change temporary values once size is defined laters */ - const QSize iconSize = QSize(24, 24); + const int iconWidth = CSS::ZimItemWidget::QLabel::lineHeight; + const QSize iconSize = QSize(iconWidth, iconWidth); iconLabel->setPixmap(icon.pixmap(iconSize)); layout()->addWidget(iconLabel); From 37c1c38824318bce0d7b05e692edc4eaf43778d1 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 30 Oct 2024 03:12:09 -0400 Subject: [PATCH 08/11] Proper Styling for MultiZim List --- resources/css/style.css | 34 ++++++++++++++++++++++++++++++++++ src/css_constants.h | 8 ++++++++ src/multizimbutton.cpp | 32 +++++++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/resources/css/style.css b/resources/css/style.css index b7e21d67..e3b08c85 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -82,11 +82,45 @@ MultiZimButton QListWidget { background-color: white; } +MultiZimButton QListWidget::item { + padding: 0px 5px; /* XXX: duplicated in css_constants.h */ + border: 1px solid transparent; /* XXX: duplicated in css_constants.h */ + background-color: white; +} + +MultiZimButton QListWidget::item:hover, +MultiZimButton QListWidget::item:selected:active { + border: 1px solid #3366CC; + background-color: #D9E9FF; +} + +MultiZimButton QScrollBar { + width: 5px; /* XXX: duplicated in css_constants.h */ + border: none; + outline: none; +} + +MultiZimButton QScrollBar::handle { + background-color: grey; +} + +ZimItemWidget * { + background-color: transparent; +} + ZimItemWidget QLabel { font-size: 16px; line-height: 24px; /* XXX: duplicated in css_constants.h */ } +ZimItemWidget QRadioButton::indicator { + image: none; +} + +ZimItemWidget QRadioButton::indicator:checked { + image: url(:/icons/tick.svg); +} + TopWidget QToolButton:pressed, TopWidget QToolButton::hover { border: 1px solid #3366CC; diff --git a/src/css_constants.h b/src/css_constants.h index 2df0acf4..337b0f63 100644 --- a/src/css_constants.h +++ b/src/css_constants.h @@ -30,8 +30,16 @@ namespace SearchBar{ namespace MultiZimButton { namespace QListWidget { +namespace item { + const int paddingHorizontal = 5; + const int border = 1; +} const int paddingVertical = 5; } + +namespace QScrollBar { + const int width = 5; +} } namespace ZimItemWidget { diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index 869c3820..963d2ad1 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -122,15 +122,41 @@ ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) : radioBt(new QRadioButton(this)) { setLayout(new QHBoxLayout); - layout()->setSpacing(0); - layout()->setContentsMargins(0, 0, 0, 0); - textLabel->setText(text); + const int paddingHorizontal = CSS::MultiZimButton::QListWidget::item::paddingHorizontal; + layout()->setSpacing(paddingHorizontal); + layout()->setContentsMargins(0, 0, 0, 0); const int iconWidth = CSS::ZimItemWidget::QLabel::lineHeight; const QSize iconSize = QSize(iconWidth, iconWidth); iconLabel->setPixmap(icon.pixmap(iconSize)); + /* Align text on same side irregardless of text direction. */ + const bool needAlignReverse = KiwixApp::isRightToLeft() == text.isRightToLeft(); + const auto align = needAlignReverse ? Qt::AlignLeft : Qt::AlignRight; + textLabel->setAlignment({Qt::AlignVCenter | align}); + + /* Need to align checkmark with select all button. Avoid scroller from + changing checkmark position by always leaving out space on scroller's + side. Do this by align items to the other side and reducing the total + length. textLabel is the only expandable element here. + + We set textLabel width to make sure the entire length always leave out + a fixed amount of white space for scroller. + */ + layout()->setAlignment({Qt::AlignVCenter | Qt::AlignLeading}); + const auto menu = KiwixApp::instance()->getSearchBar().getMultiZimButton().menu(); + const int iconAndCheckerWidth = iconWidth * 2; + const int totalSpacing = paddingHorizontal * 4; + + /* Add an extra border to counteract item border on one side */ + const int border = CSS::MultiZimButton::QListWidget::item::border; + const int scrollerWidth = CSS::MultiZimButton::QScrollBar::width; + const int contentWidthExcludeText = iconAndCheckerWidth + totalSpacing + scrollerWidth + border; + + textLabel->setFixedWidth(menu->width() - contentWidthExcludeText); + textLabel->setText(text); + layout()->addWidget(iconLabel); layout()->addWidget(textLabel); layout()->addWidget(radioBt); From 4beac3375d5364db4d3a6c46ce3c648be84b229e Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Sun, 3 Nov 2024 01:48:04 -0400 Subject: [PATCH 09/11] Introduce getElidedText() in suggestionlistdelegate.cpp Helper function to retrieve text content that can fit inside a length given a custom "(...)" elide. --- src/suggestionlistdelegate.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/suggestionlistdelegate.cpp b/src/suggestionlistdelegate.cpp index 954f7782..8ff8fbd2 100644 --- a/src/suggestionlistdelegate.cpp +++ b/src/suggestionlistdelegate.cpp @@ -44,6 +44,26 @@ void SuggestionListDelegate::paintIcon(QPainter *p, p->drawPixmap(pixmapRect, pixmap); } +/** + * @brief Get the elided text using font that can fit inside the length when + * appended with the custom elide text "(...)". + * + * @param font + * @param textRect + * @param text + * @return QString the elided text without any marker. + */ +QString getElidedText(const QFont& font, int length, const QString& text) +{ + const QFontMetrics metrics(font); + const int elideMarkerLength = metrics.tightBoundingRect("(...)").width(); + const int textLength = length - elideMarkerLength; + QString elidedText = metrics.elidedText(text, Qt::ElideRight, textLength); + if (elidedText != text) + return elidedText.chopped(1); + return text; +} + void SuggestionListDelegate::paintText(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const @@ -70,15 +90,9 @@ void SuggestionListDelegate::paintText(QPainter *p, const QString text = index.data(Qt::DisplayRole).toString(); /* Custom text elide. */ - const QFontMetrics metrics = opt.fontMetrics; - const int elideMarkerLength = metrics.tightBoundingRect("(...)").width(); - const int textLength = textRect.width() - elideMarkerLength; - QString elidedText = metrics.elidedText(text, Qt::ElideRight, textLength); + QString elidedText = getElidedText(opt.font, textRect.width(), text); if (elidedText != text) { - /* Remove built-in elide marker */ - elidedText.chop(1); - /* drawText's Align direction determines text direction */ const bool textDirFlipped = KiwixApp::isRightToLeft() != text.isRightToLeft(); elidedText = textDirFlipped ? "(...)" + elidedText.trimmed() From 14d7cff991eeffaefad1d03ecb8233eb6dcac291 Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Wed, 30 Oct 2024 03:16:13 -0400 Subject: [PATCH 10/11] Proper Elide for MultiZim List Add Custom elide marker --- src/multizimbutton.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/multizimbutton.cpp b/src/multizimbutton.cpp index 963d2ad1..25b5379b 100644 --- a/src/multizimbutton.cpp +++ b/src/multizimbutton.cpp @@ -7,6 +7,8 @@ #include "multizimbutton.h" #include "css_constants.h" +QString getElidedText(const QFont& font, int length, const QString& text); + MultiZimButton::MultiZimButton(QWidget *parent) : QToolButton(parent), mp_buttonList(new QListWidget), @@ -153,9 +155,11 @@ ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) : const int border = CSS::MultiZimButton::QListWidget::item::border; const int scrollerWidth = CSS::MultiZimButton::QScrollBar::width; const int contentWidthExcludeText = iconAndCheckerWidth + totalSpacing + scrollerWidth + border; + const int labelWidth = menu->width() - contentWidthExcludeText; + textLabel->setFixedWidth(labelWidth); - textLabel->setFixedWidth(menu->width() - contentWidthExcludeText); - textLabel->setText(text); + QString elidedText = getElidedText(textLabel->font(), labelWidth, text); + textLabel->setText(elidedText == text ? text : elidedText.trimmed() + "(...)"); layout()->addWidget(iconLabel); layout()->addWidget(textLabel); From 5922f38e75eca5544b16591bc25f15d88e3fcf6e Mon Sep 17 00:00:00 2001 From: ShaopengLin Date: Thu, 31 Oct 2024 21:57:18 -0400 Subject: [PATCH 11/11] Allow Search On Any Tab. With MultiZim, searching on library, settings, and blank tabs are reasonable. --- src/searchbar.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/searchbar.cpp b/src/searchbar.cpp index b80bea15..b3da94c6 100644 --- a/src/searchbar.cpp +++ b/src/searchbar.cpp @@ -237,8 +237,8 @@ void SearchBarLineEdit::updateCompletion() { mp_typingTimer->stop(); clearSuggestions(); - WebView* current = KiwixApp::instance()->getTabWidget()->currentWebView(); - if (!current || current->url().isEmpty() || m_searchbarInput.isEmpty()) { + const auto& multiZim = KiwixApp::instance()->getSearchBar().getMultiZimButton(); + if (multiZim.getZimIds().isEmpty() || m_searchbarInput.isEmpty()) { hideSuggestions(); return; } @@ -311,7 +311,9 @@ void SearchBarLineEdit::openCompletion(const QModelIndex &index) if (index.isValid()) { const QUrl url = index.data(Qt::UserRole).toUrl(); - QTimer::singleShot(0, [=](){KiwixApp::instance()->openUrl(url, false);}); + const auto app = KiwixApp::instance(); + const bool newTab = app->getTabWidget()->currentWebView() == nullptr; + QTimer::singleShot(0, [=](){KiwixApp::instance()->openUrl(url, newTab);}); } }