Merge pull request #110 from kiwix/language_filtering

Language filtering
This commit is contained in:
Matthieu Gautier 2019-03-06 18:48:56 +01:00 committed by GitHub
commit 0824c86c91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 403 additions and 67 deletions

View File

@ -58,7 +58,8 @@ SOURCES += \
src/contentmanagerview.cpp \ src/contentmanagerview.cpp \
src/tabbar.cpp \ src/tabbar.cpp \
src/contentmanagerside.cpp \ src/contentmanagerside.cpp \
src/readinglistbar.cpp src/readinglistbar.cpp \
src/klistwidgetitem.cpp
HEADERS += \ HEADERS += \
src/mainwindow.h \ src/mainwindow.h \
@ -78,7 +79,8 @@ HEADERS += \
src/contentmanagerview.h \ src/contentmanagerview.h \
src/tabbar.h \ src/tabbar.h \
src/contentmanagerside.h \ src/contentmanagerside.h \
src/readinglistbar.h src/readinglistbar.h \
src/klistwidgetitem.h
FORMS += \ FORMS += \
ui/mainwindow.ui \ ui/mainwindow.ui \

View File

@ -200,7 +200,6 @@ QTabBar::close-button {
border: none; border: none;
} }
#contentmanagerside QRadioButton::indicator { #contentmanagerside QRadioButton::indicator {
image: none; image: none;
} }
@ -209,6 +208,28 @@ QTabBar::close-button {
image: none; image: none;
} }
#contentmanagerside QListWidget {
border: none;
padding-left: 30px;
show-decoration-selected: 0;
}
#languageSelector QScrollBar {
width: 5px;
border: none;
outline: none;
}
#languageSelector QScrollBar::handle {
background-color: grey;
}
#languageSelector::item:selected {
background-color: white;
color: black;
outline: none;
}
#readinglistbar QListWidget { #readinglistbar QListWidget {
border: none; border: none;
} }

View File

@ -34,6 +34,7 @@ function onBooksChanged () {
var id = contentManager.bookIds[i]; var id = contentManager.bookIds[i];
contentManager.getBookInfos(id, BOOK_KEYS, addBook); contentManager.getBookInfos(id, BOOK_KEYS, addBook);
} }
app.displayedBooksNb = 20;
} }
downloadUpdaters = {} downloadUpdaters = {}
@ -59,6 +60,7 @@ function init() {
el: "#app", el: "#app",
data: { data: {
contentManager: contentManager, contentManager: contentManager,
displayedBooksNb: 20,
books: [], books: [],
downloads: {} downloads: {}
}, },
@ -72,6 +74,10 @@ function init() {
downloadUpdaters[book.id] = setInterval(function() { getDownloadInfo(book.id); }, 1000); downloadUpdaters[book.id] = setInterval(function() { getDownloadInfo(book.id); }, 1000);
}); });
}, },
displayedBooks : function(books, nb) {
var a = books.slice(0, nb);
return a;
},
niceBytes : niceBytes niceBytes : niceBytes
} }
}); });
@ -85,11 +91,25 @@ function setSearch(value) {
clearTimeout(futurCall); clearTimeout(futurCall);
futurCall = setTimeout(function(){contentManager.setSearch(value)}, 100); futurCall = setTimeout(function(){contentManager.setSearch(value)}, 100);
} }
function scrolled(e) {
if (e.offsetHeight + e.scrollTop >= e.scrollHeight) {
app.displayedBooksNb = Math.min(app.displayedBooksNb+20, app.books.length);
}
}
</script> </script>
<style> <style>
body { html, body {
padding: 0; padding: 0;
margin: 0; margin: 0;
height: 100%;
position: relative;
width: 100%;
overflow: hidden;
}
#app {
height: 100%;
position: relative;
} }
*:focus { *:focus {
outline: none; outline: none;
@ -106,9 +126,21 @@ padding: 0;
width: 90%; width: 90%;
border: 1px solid #EEE; border: 1px solid #EEE;
} }
#bookList {
width:100%; #bookTable {
position: relative;
height: calc(100% - 42px); /* 42px = 40px(height of #searchInput) + 2px(border) */
width: 100%;
} }
#bookList {
height: calc(100% - 19px - 20px); /*19px the header size, 20px the header margin-top */
overflow-y:scroll;
overflow-x:hidden;
position: relative;
width: 100%
}
.tablerow, .tablerow,
.header { .header {
display: flex; display: flex;
@ -181,7 +213,7 @@ details:hover {
<input id="searchInput" type="text" placeholder="Search files" oninput="setSearch(this.value)"/> <input id="searchInput" type="text" placeholder="Search files" oninput="setSearch(this.value)"/>
</form> </form>
</div> </div>
<div id="bookList"> <div id="bookTable">
<div class="header"> <div class="header">
<span class="tablecell cell0"></span> <span class="tablecell cell0"></span>
<span class="tablecell cell1">File name</span> <span class="tablecell cell1">File name</span>
@ -190,38 +222,40 @@ details:hover {
<span class="tablecell cell4">Content type</span> <span class="tablecell cell4">Content type</span>
<span class="tablecell cell5"></span> <span class="tablecell cell5"></span>
</div> </div>
<details v-for="book in books" class="book"> <div id="bookList" onscroll=scrolled(this)>
<summary class="tablerow"> <details v-for="book in displayedBooks(books, displayedBooksNb)" class="book">
<span class="tablecell cell0"> <summary class="tablerow">
<img v-if="book.faviconUrl" v-bind:src="'http://' + book.faviconUrl" /> <span class="tablecell cell0">
<img v-else-if="book.faviconMimeType" v-bind:src="'zim://' + book.id + '.favicon.meta'" /> <img v-if="book.faviconUrl" v-bind:src="'http://' + book.faviconUrl" />
</span> <img v-else-if="book.faviconMimeType" v-bind:src="'zim://' + book.id + '.favicon.meta'" />
<span class="tablecell cell1"> </span>
{{ book.title }} <span class="tablecell cell1">
</span> {{ book.title }}
<span class="tablecell cell2"> </span>
{{ niceBytes(book.size) }} <span class="tablecell cell2">
</span> {{ niceBytes(book.size) }}
<span class="tablecell cell3"> </span>
{{ book.date }} <span class="tablecell cell3">
</span> {{ book.date }}
<span class="tablecell cell4"> </span>
{{ book.tags }} <span class="tablecell cell4">
</span> {{ book.tags }}
<span class="tablecell cell5"> </span>
<template v-if="book.downloadId"> <span class="tablecell cell5">
<span v-if="downloads[book.id]"> <template v-if="book.downloadId">
{{ niceBytes(downloads[book.id].completedLength) }} / {{ niceBytes(downloads[book.id].totalLength) }} <span v-if="downloads[book.id]">
</span> {{ niceBytes(downloads[book.id].completedLength) }} / {{ niceBytes(downloads[book.id].totalLength) }}
</template> </span>
<button v-else-if="book.path" v-on:click="openBook(book)">Open</button> </template>
<button v-else v-on:click="downloadBook(book)">Download</button> <button v-else-if="book.path" v-on:click="openBook(book)">Open</button>
</span> <button v-else v-on:click="downloadBook(book)">Download</button>
</summary> </span>
<p class="content"> </summary>
{{ book.description }} <p class="content">
</p> {{ book.description }}
</details> </p>
</details>
</div>
</div> </div>
</div> </div>
</body></html> </body></html>

View File

@ -212,13 +212,16 @@ void ContentManager::setCurrentLanguage(QString language)
} }
m_currentLanguage = language; m_currentLanguage = language;
emit(currentLangChanged()); emit(currentLangChanged());
emit(remoteParamsChanged());
} }
#define CATALOG_HOST "library.kiwix.org" #define CATALOG_HOST "library.kiwix.org"
#define CATALOG_PORT 80 #define CATALOG_PORT 80
void ContentManager::updateRemoteLibrary() { void ContentManager::updateRemoteLibrary() {
QUrlQuery query; QUrlQuery query;
query.addQueryItem("lang", m_currentLanguage); if (m_currentLanguage != "*") {
query.addQueryItem("lang", m_currentLanguage);
}
query.addQueryItem("count", QString::number(0)); query.addQueryItem("count", QString::number(0));
QUrl url; QUrl url;
url.setScheme("http"); url.setScheme("http");

View File

@ -22,6 +22,7 @@ public:
ContentManagerView* getView() { return mp_view; } ContentManagerView* getView() { return mp_view; }
void setLocal(bool local); void setLocal(bool local);
QStringList getDownloadIds(); QStringList getDownloadIds();
void setCurrentLanguage(QString language);
private: private:
Library* mp_library; Library* mp_library;
kiwix::Library m_remoteLibrary; kiwix::Library m_remoteLibrary;
@ -30,7 +31,7 @@ private:
bool m_local = true; bool m_local = true;
QString m_currentLanguage; QString m_currentLanguage;
QString m_searchQuery; QString m_searchQuery;
void setCurrentLanguage(QString language);
QStringList getBookIds(); QStringList getBookIds();

View File

@ -1,6 +1,10 @@
#include "contentmanagerside.h" #include "contentmanagerside.h"
#include "ui_contentmanagerside.h" #include "ui_contentmanagerside.h"
#include <QLocale>
#include "klistwidgetitem.h"
ContentManagerSide::ContentManagerSide(QWidget *parent) : ContentManagerSide::ContentManagerSide(QWidget *parent) :
QWidget(parent), QWidget(parent),
mp_ui(new Ui::contentmanagerside) mp_ui(new Ui::contentmanagerside)
@ -17,9 +21,201 @@ ContentManagerSide::ContentManagerSide(QWidget *parent) :
this, [=](bool checked) { mp_ui->localFileButton->setStyleSheet( this, [=](bool checked) { mp_ui->localFileButton->setStyleSheet(
checked ?"*{font-weight: bold}" : "");}); checked ?"*{font-weight: bold}" : "");});
mp_ui->localFileButton->setStyleSheet("*{font-weight: bold}"); mp_ui->localFileButton->setStyleSheet("*{font-weight: bold}");
mp_languageButton = mp_ui->languageButton;
mp_languageSelector = mp_ui->languageSelector;
connect(mp_languageButton, &QCheckBox::toggled, this, [=](bool checked) { mp_languageSelector->setHidden(!checked); });
mp_languageSelector->setHidden(true);
for (auto lang:
{
QLocale::AnyLanguage,
QLocale::Afar,
QLocale::Afrikaans,
QLocale::Akan,
QLocale::Amharic,
QLocale::Arabic,
QLocale::Assamese,
QLocale::Azerbaijani,
QLocale::Bashkir,
QLocale::Belarusian,
QLocale::Bulgarian,
QLocale::Bambara,
QLocale::Bengali,
QLocale::Tibetan,
QLocale::Breton,
QLocale::Bosnian,
QLocale::Catalan,
QLocale::Chechen,
QLocale::Corsican,
QLocale::Czech,
QLocale::Church,
QLocale::Chuvash,
QLocale::Welsh,
QLocale::Danish,
QLocale::German,
QLocale::Divehi,
QLocale::Dzongkha,
QLocale::Ewe,
QLocale::Greek,
QLocale::English,
QLocale::Spanish,
QLocale::Estonian,
QLocale::Basque,
QLocale::Persian,
QLocale::Fulah,
QLocale::Finnish,
QLocale::Faroese,
QLocale::French,
QLocale::WesternFrisian,
QLocale::Irish,
QLocale::Gaelic,
QLocale::Galician,
QLocale::Guarani,
QLocale::Gujarati,
QLocale::Manx,
QLocale::Hausa,
QLocale::Hebrew,
QLocale::Hindi,
QLocale::Croatian,
QLocale::Hungarian,
QLocale::Armenian,
QLocale::Interlingua,
QLocale::Indonesian,
QLocale::Igbo,
QLocale::Icelandic,
QLocale::Italian,
QLocale::Inuktitut,
QLocale::Japanese,
QLocale::Javanese,
QLocale::Georgian,
QLocale::Kikuyu,
QLocale::Kazakh,
QLocale::Greenlandic,
QLocale::Khmer,
QLocale::Kannada,
QLocale::Korean,
QLocale::Kashmiri,
QLocale::Kurdish,
QLocale::Cornish,
QLocale::Kirghiz,
QLocale::Luxembourgish,
QLocale::Ganda,
QLocale::Lingala,
QLocale::Lao,
QLocale::Lithuanian,
QLocale::Latvian,
QLocale::Malagasy,
QLocale::Maori,
QLocale::Maori,
QLocale::Macedonian,
QLocale::Malayalam,
QLocale::Mongolian,
QLocale::Marathi,
QLocale::Malay,
QLocale::Maltese,
QLocale::Burmese,
QLocale::Nepali,
QLocale::Dutch,
QLocale::NorwegianNynorsk,
QLocale::NorwegianBokmal,
QLocale::Nyanja,
QLocale::Occitan,
QLocale::Oromo,
QLocale::Oriya,
QLocale::Ossetic,
QLocale::Punjabi,
QLocale::Polish,
QLocale::Pashto,
QLocale::Portuguese,
QLocale::Quechua,
QLocale::Romansh,
QLocale::Rundi,
QLocale::Romanian,
QLocale::Russian,
QLocale::Kinyarwanda,
QLocale::Sanskrit,
QLocale::Sindhi,
QLocale::NorthernSami,
QLocale::Sango,
QLocale::Sinhala,
QLocale::Slovak,
QLocale::Slovenian,
QLocale::Shona,
QLocale::Somali,
QLocale::Albanian,
QLocale::Serbian,
QLocale::Swati,
QLocale::SouthernSotho,
QLocale::Swedish,
QLocale::Swahili,
QLocale::Tamil,
QLocale::Telugu,
QLocale::Tajik,
QLocale::Thai,
QLocale::Tigrinya,
QLocale::Turkmen,
QLocale::Filipino,
QLocale::Tswana,
QLocale::Tongan,
QLocale::Turkish,
QLocale::Tsonga,
QLocale::Tatar,
QLocale::Uighur,
QLocale::Ukrainian,
QLocale::Urdu,
QLocale::Uzbek,
QLocale::Venda,
QLocale::Vietnamese,
QLocale::Walloon,
QLocale::Wolof,
QLocale::Xhosa,
QLocale::Yoruba,
QLocale::Chinese,
QLocale::Zulu,
})
{
auto currentLang = QLocale().language();
if (lang == QLocale::AnyLanguage) {
auto item = new KListWidgetItem("All");
item->setData(Qt::UserRole, lang);
mp_languageSelector->addItem(item);
continue;
}
auto locale = QLocale(lang);
if (locale.language() != lang) {
// Qt may not find the locale for the lang :/
// In this case, Qt return the current locale
// So we must be sure that the locale found correspond to the lang we want to add,
// else we may add several time the current language.
continue;
}
auto item = new KListWidgetItem(QLocale::languageToString(locale.language()));
item->setData(Qt::UserRole, lang);
mp_languageSelector->addItem(item);
if (lang == currentLang) {
item->setSelected(true);
}
}
} }
ContentManagerSide::~ContentManagerSide() ContentManagerSide::~ContentManagerSide()
{ {
delete mp_ui; delete mp_ui;
} }
void ContentManagerSide::setContentManager(ContentManager *contentManager)
{
mp_contentManager = contentManager;
connect(mp_languageSelector, &QListWidget::itemSelectionChanged,
this, [=]() {
auto item = mp_languageSelector->selectedItems().at(0);
if (!item) return;
auto lang = QLocale::Language(item->data(Qt::UserRole).toInt());
if (lang == QLocale::AnyLanguage) {
mp_contentManager->setCurrentLanguage("*");
return;
}
auto locale = QLocale(QLocale::Language(item->data(Qt::UserRole).toInt()));
mp_contentManager->setCurrentLanguage(locale.name().split("_").at(0));});
}

View File

@ -2,6 +2,8 @@
#define CONTENTMANAGERSIDE_H #define CONTENTMANAGERSIDE_H
#include <QWidget> #include <QWidget>
#include <QListWidget>
#include <QCheckBox>
#include "contentmanager.h" #include "contentmanager.h"
namespace Ui { namespace Ui {
@ -16,11 +18,13 @@ public:
explicit ContentManagerSide(QWidget *parent = 0); explicit ContentManagerSide(QWidget *parent = 0);
~ContentManagerSide(); ~ContentManagerSide();
void setContentManager(ContentManager* contentManager) { mp_contentManager = contentManager; } void setContentManager(ContentManager* contentManager);
private: private:
Ui::contentmanagerside *mp_ui; Ui::contentmanagerside *mp_ui;
ContentManager* mp_contentManager; ContentManager* mp_contentManager;
QCheckBox* mp_languageButton;
QListWidget* mp_languageSelector;
}; };
#endif // CONTENTMANAGERSIDE_H #endif // CONTENTMANAGERSIDE_H

View File

@ -10,6 +10,12 @@
<height>366</height> <height>366</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
@ -37,10 +43,13 @@
<property name="flat"> <property name="flat">
<bool>true</bool> <bool>true</bool>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,0,0,0,1">
<property name="spacing"> <property name="spacing">
<number>0</number> <number>0</number>
</property> </property>
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -70,6 +79,50 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="languageButton">
<property name="text">
<string>Browse By Language</string>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="languageSelector">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="cursor" stdset="0">
<cursorShape>ArrowCursor</cursorShape>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectItems</enum>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="categoryButton"> <widget class="QCheckBox" name="categoryButton">
<property name="enabled"> <property name="enabled">
@ -80,16 +133,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="languageButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="contentTypeButton"> <widget class="QCheckBox" name="contentTypeButton">
<property name="enabled"> <property name="enabled">
@ -100,22 +143,22 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

19
src/klistwidgetitem.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "klistwidgetitem.h"
KListWidgetItem::KListWidgetItem(QString text)
: QListWidgetItem (text)
{
}
QVariant KListWidgetItem::data(int role) const
{
QVariant v = QListWidgetItem::data(role);
if( isSelected() && role == Qt::FontRole )
{
QFont font = v.value<QFont>();
font.setBold( true );
v = QVariant::fromValue<QFont>( font );
}
return v;
}

13
src/klistwidgetitem.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef KLISTWIDGETITEM_H
#define KLISTWIDGETITEM_H
#include <QListWidgetItem>
class KListWidgetItem : public QListWidgetItem
{
public:
KListWidgetItem(QString text);
QVariant data(int role) const;
};
#endif // KLISTWIDGETITEM_H