Skip to content

Commit 2365249

Browse files
authored
Merge pull request #1218 from kiwix/Issue#413-multizim
UI for selecting the book to be used for title search
2 parents bdfcc88 + 5922f38 commit 2365249

File tree

14 files changed

+332
-20
lines changed

14 files changed

+332
-20
lines changed

kiwix-desktop.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ SOURCES += \
9191
src/fullscreenwindow.cpp \
9292
src/fullscreennotification.cpp \
9393
src/zimview.cpp \
94+
src/multizimbutton.cpp \
9495

9596
HEADERS += \
9697
src/choiceitem.h \
@@ -143,6 +144,7 @@ HEADERS += \
143144
src/zimview.h \
144145
src/portutils.h \
145146
src/css_constants.h \
147+
src/multizimbutton.h \
146148

147149
FORMS += \
148150
src/choiceitem.ui \

resources/css/style.css

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,52 @@ SearchBar > QToolButton:hover {
7575
border-radius: 3px;
7676
}
7777

78+
MultiZimButton QListWidget {
79+
border: 0px;
80+
outline: 0px;
81+
padding: 5px 0px; /* XXX: duplicated in css_constants.h */
82+
background-color: white;
83+
}
84+
85+
MultiZimButton QListWidget::item {
86+
padding: 0px 5px; /* XXX: duplicated in css_constants.h */
87+
border: 1px solid transparent; /* XXX: duplicated in css_constants.h */
88+
background-color: white;
89+
}
90+
91+
MultiZimButton QListWidget::item:hover,
92+
MultiZimButton QListWidget::item:selected:active {
93+
border: 1px solid #3366CC;
94+
background-color: #D9E9FF;
95+
}
96+
97+
MultiZimButton QScrollBar {
98+
width: 5px; /* XXX: duplicated in css_constants.h */
99+
border: none;
100+
outline: none;
101+
}
102+
103+
MultiZimButton QScrollBar::handle {
104+
background-color: grey;
105+
}
106+
107+
ZimItemWidget * {
108+
background-color: transparent;
109+
}
110+
111+
ZimItemWidget QLabel {
112+
font-size: 16px;
113+
line-height: 24px; /* XXX: duplicated in css_constants.h */
114+
}
115+
116+
ZimItemWidget QRadioButton::indicator {
117+
image: none;
118+
}
119+
120+
ZimItemWidget QRadioButton::indicator:checked {
121+
image: url(:/icons/tick.svg);
122+
}
123+
78124
TopWidget QToolButton:pressed,
79125
TopWidget QToolButton::hover {
80126
border: 1px solid #3366CC;

resources/i18n/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,5 +172,6 @@
172172
"portable-disabled-tooltip": "Function disabled in portable mode",
173173
"scroll-next-tab": "Scroll to next tab",
174174
"scroll-previous-tab": "Scroll to previous tab",
175-
"kiwix-search": "Kiwix search"
175+
"kiwix-search": "Kiwix search",
176+
"search-options": "Search Options"
176177
}

resources/i18n/qqq.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,6 @@
180180
"portable-disabled-tooltip": "Tooltip used to explain disabled components in the portable version.",
181181
"scroll-next-tab": "Represents the action of scrolling to the next tab of the current tab which toward the end of the tab bar.",
182182
"scroll-previous-tab": "Represents the action of scrolling to the previous tab of the current tab which toward the start of the tab bar.",
183-
"kiwix-search": "Title text for a list of search results, which notes to the user those are from Kiwix's Search Engine"
183+
"kiwix-search": "Title text for a list of search results, which notes to the user those are from Kiwix's Search Engine",
184+
"search-options": "The term for a collection of additional search filtering options to help users narrow down search results."
184185
}

src/css_constants.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,26 @@ namespace SearchBar{
2828
const int border = 1;
2929
}
3030

31+
namespace MultiZimButton {
32+
namespace QListWidget {
33+
namespace item {
34+
const int paddingHorizontal = 5;
35+
const int border = 1;
36+
}
37+
const int paddingVertical = 5;
38+
}
39+
40+
namespace QScrollBar {
41+
const int width = 5;
42+
}
43+
}
44+
45+
namespace ZimItemWidget {
46+
namespace QLabel {
47+
const int lineHeight = 24;
48+
}
49+
}
50+
3151
namespace TopWidget {
3252
namespace QToolButton {
3353
namespace backButton {

src/kiwixapp.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,8 @@ void KiwixApp::createActions()
438438
CREATE_ACTION_SHORTCUT(ToggleTOCAction, gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_1));
439439
HIDE_ACTION(ToggleTOCAction);
440440

441+
CREATE_ACTION_ICON_SHORTCUT(OpenMultiZimAction, "filter", gt("search-options"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_L));
442+
441443
CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL | Qt::Key_B));
442444

443445
CREATE_ACTION(ExportReadingListAction, gt("export-reading-list"));

src/kiwixapp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class KiwixApp : public QtSingleApplication
3939
BrowseLibraryAction,
4040
OpenFileAction,
4141
OpenRecentAction,
42+
OpenMultiZimAction,
4243
SavePageAsAction,
4344
SearchArticleAction,
4445
SearchLibraryAction,

src/mainmenu.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ MainMenu::MainMenu(QWidget *parent) :
3535
m_editMenu.ADD_ACTION(SearchLibraryAction);
3636
m_editMenu.ADD_ACTION(FindInPageAction);
3737
m_editMenu.ADD_ACTION(ToggleAddBookmarkAction);
38+
m_editMenu.ADD_ACTION(OpenMultiZimAction);
3839
addMenu(&m_editMenu);
3940

4041
m_viewMenu.setTitle(gt("view"));

src/multizimbutton.cpp

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include <QListWidget>
2+
#include <QMenu>
3+
#include <QWidgetAction>
4+
#include <QButtonGroup>
5+
#include <QRadioButton>
6+
#include "kiwixapp.h"
7+
#include "multizimbutton.h"
8+
#include "css_constants.h"
9+
10+
QString getElidedText(const QFont& font, int length, const QString& text);
11+
12+
MultiZimButton::MultiZimButton(QWidget *parent) :
13+
QToolButton(parent),
14+
mp_buttonList(new QListWidget),
15+
mp_radioButtonGroup(new QButtonGroup(this))
16+
{
17+
setMenu(new QMenu(this));
18+
setPopupMode(QToolButton::InstantPopup);
19+
setDefaultAction(KiwixApp::instance()->getAction(KiwixApp::OpenMultiZimAction));
20+
connect(this, &QToolButton::triggered, this, &MultiZimButton::showMenu);
21+
22+
const auto popupAction = new QWidgetAction(menu());
23+
popupAction->setDefaultWidget(mp_buttonList);
24+
menu()->addAction(popupAction);
25+
26+
connect(mp_buttonList, &QListWidget::currentRowChanged, this, [=](int row){
27+
if (const auto widget = getZimWidget(row))
28+
widget->getRadioButton()->setChecked(true);
29+
});
30+
}
31+
32+
void MultiZimButton::updateDisplay()
33+
{
34+
mp_buttonList->clear();
35+
for (const auto& button : mp_radioButtonGroup->buttons())
36+
mp_radioButtonGroup->removeButton(button);
37+
38+
const auto library = KiwixApp::instance()->getLibrary();
39+
const auto view = KiwixApp::instance()->getTabWidget()->currentWebView();
40+
QListWidgetItem* currentItem = nullptr;
41+
QIcon currentIcon;
42+
const int paddingTopBot = CSS::MultiZimButton::QListWidget::paddingVertical * 2;
43+
const int itemHeight = paddingTopBot + CSS::ZimItemWidget::QLabel::lineHeight;
44+
for (const auto& bookId : library->getBookIds())
45+
{
46+
try
47+
{
48+
library->getArchive(bookId);
49+
} catch (...) { continue; }
50+
51+
const QString bookTitle = QString::fromStdString(library->getBookById(bookId).getTitle());
52+
const QIcon zimIcon = library->getBookIcon(bookId);
53+
54+
const auto item = new QListWidgetItem();
55+
item->setData(Qt::UserRole, bookId);
56+
item->setData(Qt::DisplayRole, bookTitle);
57+
item->setSizeHint(QSize(0, itemHeight));
58+
59+
if (view && view->zimId() == bookId)
60+
{
61+
currentItem = item;
62+
currentIcon = zimIcon;
63+
continue;
64+
}
65+
66+
mp_buttonList->addItem(item);
67+
setItemZimWidget(item, bookTitle, zimIcon);
68+
}
69+
70+
mp_buttonList->sortItems();
71+
if (currentItem)
72+
{
73+
mp_buttonList->insertItem(0, currentItem);
74+
75+
const auto title = currentItem->data(Qt::DisplayRole).toString();
76+
setItemZimWidget(currentItem, "*" + title, currentIcon);
77+
}
78+
79+
/* Display should not be used other than for sorting. */
80+
for (int i = 0; i < mp_buttonList->count(); i++)
81+
mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant());
82+
83+
setDisabled(mp_buttonList->model()->rowCount() == 0);
84+
85+
mp_buttonList->scrollToTop();
86+
mp_buttonList->setCurrentRow(0);
87+
88+
/* We set a maximum display height for list. Respect padding. */
89+
const int listHeight = itemHeight * std::min(7, mp_buttonList->count());
90+
mp_buttonList->setFixedHeight(listHeight + paddingTopBot);
91+
mp_buttonList->setFixedWidth(menu()->width());
92+
}
93+
94+
QStringList MultiZimButton::getZimIds() const
95+
{
96+
QStringList idList;
97+
for (int row = 0; row < mp_buttonList->count(); row++)
98+
{
99+
const auto widget = getZimWidget(row);
100+
if (widget && widget->getRadioButton()->isChecked())
101+
idList.append(mp_buttonList->item(row)->data(Qt::UserRole).toString());
102+
}
103+
return idList;
104+
}
105+
106+
ZimItemWidget *MultiZimButton::getZimWidget(int row) const
107+
{
108+
const auto widget = mp_buttonList->itemWidget(mp_buttonList->item(row));
109+
return qobject_cast<ZimItemWidget *>(widget);
110+
}
111+
112+
void MultiZimButton::setItemZimWidget(QListWidgetItem *item,
113+
const QString &title, const QIcon &icon)
114+
{
115+
const auto zimWidget = new ZimItemWidget(title, icon);
116+
mp_radioButtonGroup->addButton(zimWidget->getRadioButton());
117+
mp_buttonList->setItemWidget(item, zimWidget);
118+
}
119+
120+
ZimItemWidget::ZimItemWidget(QString text, QIcon icon, QWidget *parent) :
121+
QWidget(parent),
122+
textLabel(new QLabel(this)),
123+
iconLabel(new QLabel(this)),
124+
radioBt(new QRadioButton(this))
125+
{
126+
setLayout(new QHBoxLayout);
127+
128+
const int paddingHorizontal = CSS::MultiZimButton::QListWidget::item::paddingHorizontal;
129+
layout()->setSpacing(paddingHorizontal);
130+
layout()->setContentsMargins(0, 0, 0, 0);
131+
132+
const int iconWidth = CSS::ZimItemWidget::QLabel::lineHeight;
133+
const QSize iconSize = QSize(iconWidth, iconWidth);
134+
iconLabel->setPixmap(icon.pixmap(iconSize));
135+
136+
/* Align text on same side irregardless of text direction. */
137+
const bool needAlignReverse = KiwixApp::isRightToLeft() == text.isRightToLeft();
138+
const auto align = needAlignReverse ? Qt::AlignLeft : Qt::AlignRight;
139+
textLabel->setAlignment({Qt::AlignVCenter | align});
140+
141+
/* Need to align checkmark with select all button. Avoid scroller from
142+
changing checkmark position by always leaving out space on scroller's
143+
side. Do this by align items to the other side and reducing the total
144+
length. textLabel is the only expandable element here.
145+
146+
We set textLabel width to make sure the entire length always leave out
147+
a fixed amount of white space for scroller.
148+
*/
149+
layout()->setAlignment({Qt::AlignVCenter | Qt::AlignLeading});
150+
const auto menu = KiwixApp::instance()->getSearchBar().getMultiZimButton().menu();
151+
const int iconAndCheckerWidth = iconWidth * 2;
152+
const int totalSpacing = paddingHorizontal * 4;
153+
154+
/* Add an extra border to counteract item border on one side */
155+
const int border = CSS::MultiZimButton::QListWidget::item::border;
156+
const int scrollerWidth = CSS::MultiZimButton::QScrollBar::width;
157+
const int contentWidthExcludeText = iconAndCheckerWidth + totalSpacing + scrollerWidth + border;
158+
const int labelWidth = menu->width() - contentWidthExcludeText;
159+
textLabel->setFixedWidth(labelWidth);
160+
161+
QString elidedText = getElidedText(textLabel->font(), labelWidth, text);
162+
textLabel->setText(elidedText == text ? text : elidedText.trimmed() + "(...)");
163+
164+
layout()->addWidget(iconLabel);
165+
layout()->addWidget(textLabel);
166+
layout()->addWidget(radioBt);
167+
}

src/multizimbutton.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#ifndef MULTIZIMBUTTON_H
2+
#define MULTIZIMBUTTON_H
3+
4+
#include <QToolButton>
5+
6+
class QListWidget;
7+
class QButtonGroup;
8+
class QListWidgetItem;
9+
class QRadioButton;
10+
class QLabel;
11+
12+
class ZimItemWidget : public QWidget {
13+
Q_OBJECT
14+
15+
public:
16+
ZimItemWidget(QString text, QIcon icon, QWidget *parent = nullptr);
17+
18+
QRadioButton* getRadioButton() const { return radioBt; }
19+
20+
private:
21+
QLabel* textLabel;
22+
QLabel* iconLabel;
23+
QRadioButton* radioBt;
24+
};
25+
26+
class MultiZimButton : public QToolButton {
27+
Q_OBJECT
28+
29+
public:
30+
explicit MultiZimButton(QWidget *parent = nullptr);
31+
32+
public slots:
33+
void updateDisplay();
34+
QStringList getZimIds() const;
35+
36+
private:
37+
QListWidget* mp_buttonList;
38+
QButtonGroup* mp_radioButtonGroup;
39+
40+
ZimItemWidget* getZimWidget(int row) const;
41+
void setItemZimWidget(QListWidgetItem* item, const QString& title, const QIcon& icon);
42+
};
43+
44+
#endif // MULTIZIMBUTTON_H

0 commit comments

Comments
 (0)