Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI for selecting the book to be used for title search #1218

Merged
merged 11 commits into from
Nov 4, 2024
2 changes: 2 additions & 0 deletions kiwix-desktop.pro
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ SOURCES += \
src/fullscreenwindow.cpp \
src/fullscreennotification.cpp \
src/zimview.cpp \
src/multizimbutton.cpp \

HEADERS += \
src/choiceitem.h \
Expand Down Expand Up @@ -143,6 +144,7 @@ HEADERS += \
src/zimview.h \
src/portutils.h \
src/css_constants.h \
src/multizimbutton.h \

FORMS += \
src/choiceitem.ui \
Expand Down
46 changes: 46 additions & 0 deletions resources/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,52 @@ SearchBar > QToolButton:hover {
border-radius: 3px;
}

MultiZimButton QListWidget {
border: 0px;
outline: 0px;
padding: 5px 0px; /* XXX: duplicated in css_constants.h */
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;
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
3 changes: 2 additions & 1 deletion resources/i18n/qqq.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
20 changes: 20 additions & 0 deletions src/css_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@ namespace SearchBar{
const int border = 1;
}

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 {
namespace QLabel {
const int lineHeight = 24;
}
}

namespace TopWidget {
namespace QToolButton {
namespace backButton {
Expand Down
2 changes: 2 additions & 0 deletions src/kiwixapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down
1 change: 1 addition & 0 deletions src/kiwixapp.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class KiwixApp : public QtSingleApplication
BrowseLibraryAction,
OpenFileAction,
OpenRecentAction,
OpenMultiZimAction,
SavePageAsAction,
SearchArticleAction,
SearchLibraryAction,
Expand Down
1 change: 1 addition & 0 deletions src/mainmenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand Down
167 changes: 167 additions & 0 deletions src/multizimbutton.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#include <QListWidget>
#include <QMenu>
#include <QWidgetAction>
#include <QButtonGroup>
#include <QRadioButton>
#include "kiwixapp.h"
#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),
mp_radioButtonGroup(new QButtonGroup(this))
{
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);
menu()->addAction(popupAction);

connect(mp_buttonList, &QListWidget::currentRowChanged, this, [=](int row){
if (const auto widget = getZimWidget(row))
widget->getRadioButton()->setChecked(true);
});
}

void MultiZimButton::updateDisplay()
{
mp_buttonList->clear();
for (const auto& button : mp_radioButtonGroup->buttons())
mp_radioButtonGroup->removeButton(button);

const auto library = KiwixApp::instance()->getLibrary();
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
{
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);
item->setSizeHint(QSize(0, itemHeight));

if (view && view->zimId() == bookId)
{
currentItem = item;
currentIcon = zimIcon;
continue;
}

mp_buttonList->addItem(item);
setItemZimWidget(item, bookTitle, zimIcon);
ShaopengLin marked this conversation as resolved.
Show resolved Hide resolved
}

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++)
mp_buttonList->item(i)->setData(Qt::DisplayRole, QVariant());

setDisabled(mp_buttonList->model()->rowCount() == 0);

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
{
QStringList idList;
for (int row = 0; row < mp_buttonList->count(); row++)
{
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<ZimItemWidget *>(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);

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;
const int labelWidth = menu->width() - contentWidthExcludeText;
textLabel->setFixedWidth(labelWidth);

QString elidedText = getElidedText(textLabel->font(), labelWidth, text);
textLabel->setText(elidedText == text ? text : elidedText.trimmed() + "(...)");

layout()->addWidget(iconLabel);
layout()->addWidget(textLabel);
layout()->addWidget(radioBt);
}
44 changes: 44 additions & 0 deletions src/multizimbutton.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef MULTIZIMBUTTON_H
#define MULTIZIMBUTTON_H

#include <QToolButton>

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

public:
explicit MultiZimButton(QWidget *parent = nullptr);

public slots:
void updateDisplay();
QStringList getZimIds() const;

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