Skip to content

Commit

Permalink
Merge pull request #1218 from kiwix/Issue#413-multizim
Browse files Browse the repository at this point in the history
UI for selecting the book to be used for title search
  • Loading branch information
kelson42 authored Nov 4, 2024
2 parents bdfcc88 + 5922f38 commit 2365249
Show file tree
Hide file tree
Showing 14 changed files with 332 additions and 20 deletions.
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);
}

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

0 comments on commit 2365249

Please sign in to comment.