Initial Qt6 support

This commit is contained in:
Adam Lamar 2024-02-26 22:18:34 +00:00
parent ef1489193e
commit d842a288f7
26 changed files with 280 additions and 63 deletions

12
.gitignore vendored
View File

@ -30,6 +30,18 @@
*.exe *.exe
*.out *.out
*.app *.app
/kiwix-desktop
# QtCreator Environment Settings (not suitable for sharing) # QtCreator Environment Settings (not suitable for sharing)
kiwix-desktop.pro.user kiwix-desktop.pro.user
# Qt autogenerated files
/qrc_*.cpp
/ui_*.h
/moc_*.h
/moc_*.cpp
/Makefile
/.qmake.stash
# IDE files
/.vscode

View File

@ -73,6 +73,38 @@ You may want to simply open the kiwix-desktop project in QtCreator and
then compile the project from there (don't forget to update then compile the project from there (don't forget to update
`PKG_CONFIG_PATH` if necessary). `PKG_CONFIG_PATH` if necessary).
Compilation with Qt6
--------------------
There is initial support for Qt6. Additional packages are needed:
```bash
sudo apt install qt6-base-dev qt6-base-dev-tools qt6-webengine-dev libqt6webenginecore6-bin libqt6svg6
```
And `qmake` needs to be configured to use Qt6. First confirm `qmake` is using the right version:
```bash
qtchooser -install qt6 $(which qmake6) # run once
export QT_SELECT=qt6 # set in environments where Qt6 builds are desired
qmake --version
```
produces this output:
```bash
$ qmake --version
QMake version 3.1
Using Qt version 6.2.4 in /usr/lib/aarch64-linux-gnu
```
then build as normal:
```bash
qmake .
make
```
Installation Installation
------------ ------------

View File

@ -30,7 +30,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs. # You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line. # In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt. # You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \ SOURCES += \
@ -124,6 +124,7 @@ HEADERS += \
src/fullscreennotification.h \ src/fullscreennotification.h \
src/menuproxystyle.h \ src/menuproxystyle.h \
src/zimview.h \ src/zimview.h \
src/portutils.h \
FORMS += \ FORMS += \
src/choiceitem.ui \ src/choiceitem.ui \

View File

@ -125,6 +125,7 @@
"ok":"ok", "ok":"ok",
"no-filter":"no filter", "no-filter":"no filter",
"open-link-in-web-browser":"Open link in web browser", "open-link-in-web-browser":"Open link in web browser",
"open-link-new-tab":"Open link in new tab",
"download-dir-dialog-title":"Are you sure you want to change the download directory?", "download-dir-dialog-title":"Are you sure you want to change the download directory?",
"download-dir-dialog-msg":"The new download directory path will be:\n{{DIRECTORY}}", "download-dir-dialog-msg":"The new download directory path will be:\n{{DIRECTORY}}",
"invalid-port":"Invalid port", "invalid-port":"Invalid port",

View File

@ -6,6 +6,7 @@
class BlobBuffer : public QBuffer class BlobBuffer : public QBuffer
{ {
Q_OBJECT
public: public:
BlobBuffer(zim::Blob m_blob); BlobBuffer(zim::Blob m_blob);
virtual ~BlobBuffer() = default; virtual ~BlobBuffer() = default;

View File

@ -316,7 +316,7 @@ ContentManager::BookInfo ContentManager::getBookInfos(QString id, const QStringL
QStringList tagList = QString::fromStdString(b->getTags()).split(';'); QStringList tagList = QString::fromStdString(b->getTags()).split(';');
QMap<QString, bool> displayTagMap; QMap<QString, bool> displayTagMap;
for(auto tag: tagList) { for(auto tag: tagList) {
if (tag[0] == "_") { if (tag[0] == '_') {
auto splitTag = tag.split(":"); auto splitTag = tag.split(":");
displayTagMap[splitTag[0]] = splitTag[1] == "yes" ? true:false; displayTagMap[splitTag[0]] = splitTag[1] == "yes" ? true:false;
} }

View File

@ -2,11 +2,12 @@
#include "contentmanagerdelegate.h" #include "contentmanagerdelegate.h"
#include <QApplication> #include <QApplication>
#include <QDialog> #include <QDialog>
#include <QStyleOptionViewItemV4> #include <QStyleOptionViewItem>
#include "kiwixapp.h" #include "kiwixapp.h"
#include <QStyleOptionViewItem> #include <QStyleOptionViewItem>
#include "rownode.h" #include "rownode.h"
#include "descriptionnode.h" #include "descriptionnode.h"
#include "portutils.h"
ContentManagerDelegate::ContentManagerDelegate(QObject *parent) ContentManagerDelegate::ContentManagerDelegate(QObject *parent)
: QStyledItemDelegate(parent), baseButton(new QPushButton) : QStyledItemDelegate(parent), baseButton(new QPushButton)
@ -198,7 +199,11 @@ void ContentManagerDelegate::paint(QPainter *painter, const QStyleOptionViewItem
} }
if (index.column() == 1) { if (index.column() == 1) {
auto bFont = painter->font(); auto bFont = painter->font();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
bFont.setWeight(60); bFont.setWeight(60);
#else
bFont.setWeight(QFont::DemiBold);
#endif
eOpt.font = bFont; eOpt.font = bFont;
} }
QStyledItemDelegate::paint(painter, eOpt, index); QStyledItemDelegate::paint(painter, eOpt, index);
@ -209,8 +214,8 @@ bool ContentManagerDelegate::editorEvent(QEvent *event, QAbstractItemModel *mode
if(event->type() == QEvent::MouseButtonRelease ) if(event->type() == QEvent::MouseButtonRelease )
{ {
QMouseEvent * e = (QMouseEvent *)event; QMouseEvent * e = (QMouseEvent *)event;
int clickX = e->x(); int clickX = portutils::getX(*e);
int clickY = e->y(); int clickY = portutils::getY(*e);
QRect r = option.rect; QRect r = option.rect;
int x,y,w,h; int x,y,w,h;
@ -238,7 +243,7 @@ void ContentManagerDelegate::handleLastColumnClicked(const QModelIndex& index, Q
{ {
const auto node = static_cast<RowNode*>(index.internalPointer()); const auto node = static_cast<RowNode*>(index.internalPointer());
const auto id = node->getBookId(); const auto id = node->getBookId();
int clickX = mouseEvent->x(); int clickX = portutils::getX(*mouseEvent);
QRect r = option.rect; QRect r = option.rect;
int x = r.left(); int x = r.left();

View File

@ -57,6 +57,7 @@
//! [0] //! [0]
class FlowLayout : public QLayout class FlowLayout : public QLayout
{ {
Q_OBJECT
public: public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1);
explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1);

View File

@ -1,3 +1,5 @@
class QMenu;
#include "fullscreenwindow.h" #include "fullscreenwindow.h"
#include <QAction> #include <QAction>
@ -41,4 +43,4 @@ void FullScreenWindow::resizeEvent(QResizeEvent *event)
m_notification->setGeometry(notificationGeometry); m_notification->setGeometry(notificationGeometry);
QWidget::resizeEvent(event); QWidget::resizeEvent(event);
} }

View File

@ -1,6 +1,7 @@
#ifndef FULLSCREENWINDOW_H #ifndef FULLSCREENWINDOW_H
#define FULLSCREENWINDOW_H #define FULLSCREENWINDOW_H
class QMenu;
#include <QWidget> #include <QWidget>
#include <QWebEngineView> #include <QWebEngineView>
#include "fullscreennotification.h" #include "fullscreennotification.h"
@ -28,4 +29,4 @@ private:
QRect m_oldGeometry; QRect m_oldGeometry;
}; };
#endif // FULLSCREENWINDOW_H #endif // FULLSCREENWINDOW_H

View File

@ -40,8 +40,13 @@ KiwixApp::KiwixApp(int& argc, char *argv[])
QMessageBox::critical(nullptr, "Translation error", e.what()); QMessageBox::critical(nullptr, "Translation error", e.what());
return; return;
} }
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
m_qtTranslator.load(QLocale(), "qt", "_", m_qtTranslator.load(QLocale(), "qt", "_",
QLibraryInfo::location(QLibraryInfo::TranslationsPath)); QLibraryInfo::location(QLibraryInfo::TranslationsPath));
#else
m_qtTranslator.load(QLocale(), "qt", "_",
QLibraryInfo::path(QLibraryInfo::TranslationsPath));
#endif
installTranslator(&m_qtTranslator); installTranslator(&m_qtTranslator);
m_appTranslator.load(QLocale(), "kiwix-desktop", "_", ":/i18n/"); m_appTranslator.load(QLocale(), "kiwix-desktop", "_", ":/i18n/");
@ -201,6 +206,7 @@ void KiwixApp::printPage()
{ {
if(!getTabWidget()->currentZimView()) if(!getTabWidget()->currentZimView())
return; return;
QPrinter* printer = new QPrinter(); QPrinter* printer = new QPrinter();
QPrintDialog printDialog(printer, mp_mainWindow); QPrintDialog printDialog(printer, mp_mainWindow);
printDialog.setStyle(nullptr); printDialog.setStyle(nullptr);
@ -209,12 +215,23 @@ void KiwixApp::printPage()
auto webview = getTabWidget()->currentWebView(); auto webview = getTabWidget()->currentWebView();
if(!webview) if(!webview)
return; return;
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
webview->page()->print(printer, [=](bool success) { webview->page()->print(printer, [=](bool success) {
if (!success) { if (!success) {
showMessage(gt("print-page-error"), gt("error-title"), QMessageBox::Critical); showMessage(gt("print-page-error"), gt("error-title"), QMessageBox::Critical);
} }
delete printer; delete printer;
}); });
#else
webview->print(printer);
connect(webview, &QWebEngineView::printFinished, this, [=](bool success) {
if (!success) {
showMessage(gt("print-page-error"), gt("error-title"), QMessageBox::Critical);
}
delete printer;
});
#endif
} }
} }
@ -329,25 +346,25 @@ void KiwixApp::setMonitorDir(const QString &dir) {
void KiwixApp::createAction() void KiwixApp::createAction()
{ {
CREATE_ACTION_ICON_SHORTCUT(KiwixServeAction, "share", gt("local-kiwix-server"), QKeySequence(Qt::CTRL+Qt::Key_I)); CREATE_ACTION_ICON_SHORTCUT(KiwixServeAction, "share", gt("local-kiwix-server"), QKeySequence(Qt::CTRL | Qt::Key_I));
CREATE_ACTION_ICON_SHORTCUT(RandomArticleAction, "random", gt("random-article"), QKeySequence(Qt::CTRL+Qt::Key_R)); CREATE_ACTION_ICON_SHORTCUT(RandomArticleAction, "random", gt("random-article"), QKeySequence(Qt::CTRL | Qt::Key_R));
connect(mpa_actions[RandomArticleAction], &QAction::triggered, connect(mpa_actions[RandomArticleAction], &QAction::triggered,
this, [=]() { this->openRandomUrl(false); }); this, [=]() { this->openRandomUrl(false); });
CREATE_ACTION_SHORTCUT(OpenHomePageAction, gt("home-page"), QKeySequence(Qt::ALT + Qt::Key_Home)); CREATE_ACTION_SHORTCUT(OpenHomePageAction, gt("home-page"), QKeySequence(Qt::ALT | Qt::Key_Home));
if (QGuiApplication::isLeftToRight()) { if (QGuiApplication::isLeftToRight()) {
CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-left", gt("back"), QKeySequence(Qt::ALT + Qt::Key_Left)); CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-left", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Left));
} else { } else {
CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-right", gt("back"), QKeySequence(Qt::ALT + Qt::Key_Right)); CREATE_ACTION_ICON_SHORTCUT(HistoryBackAction, "history-right", gt("back"), QKeySequence(Qt::ALT | Qt::Key_Right));
} }
DISABLE_ACTION(HistoryBackAction); DISABLE_ACTION(HistoryBackAction);
if (QGuiApplication::isLeftToRight()) { if (QGuiApplication::isLeftToRight()) {
CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-right", gt("forward"), QKeySequence(Qt::ALT + Qt::Key_Right)); CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-right", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Right));
} else { } else {
CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-left", gt("forward"), QKeySequence(Qt::ALT + Qt::Key_Left)); CREATE_ACTION_ICON_SHORTCUT(HistoryForwardAction, "history-left", gt("forward"), QKeySequence(Qt::ALT | Qt::Key_Left));
} }
DISABLE_ACTION(HistoryForwardAction); DISABLE_ACTION(HistoryForwardAction);
@ -357,13 +374,13 @@ void KiwixApp::createAction()
CREATE_ACTION_ICON_SHORTCUT(NewTabAction,"new-tab-icon", gt("new-tab"), QKeySequence::AddTab); CREATE_ACTION_ICON_SHORTCUT(NewTabAction,"new-tab-icon", gt("new-tab"), QKeySequence::AddTab);
CREATE_ACTION_ICON_SHORTCUTS(CloseTabAction, "close", gt("close-tab"), QList<QKeySequence>({QKeySequence(Qt::CTRL + Qt::Key_F4), QKeySequence(Qt::CTRL + Qt::Key_W)})); CREATE_ACTION_ICON_SHORTCUTS(CloseTabAction, "close", gt("close-tab"), QList<QKeySequence>({QKeySequence(Qt::CTRL | Qt::Key_F4), QKeySequence(Qt::CTRL | Qt::Key_W)}));
mpa_actions[CloseTabAction]->setIconVisibleInMenu(false); mpa_actions[CloseTabAction]->setIconVisibleInMenu(false);
CREATE_ACTION_SHORTCUT(ReopenClosedTabAction, gt("reopen-closed-tab"), QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_T)); CREATE_ACTION_SHORTCUT(ReopenClosedTabAction, gt("reopen-closed-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_T));
HIDE_ACTION(ReopenClosedTabAction); HIDE_ACTION(ReopenClosedTabAction);
CREATE_ACTION_SHORTCUT(BrowseLibraryAction, gt("browse-library"), QKeySequence(Qt::CTRL+Qt::Key_E)); CREATE_ACTION_SHORTCUT(BrowseLibraryAction, gt("browse-library"), QKeySequence(Qt::CTRL | Qt::Key_E));
HIDE_ACTION(BrowseLibraryAction); HIDE_ACTION(BrowseLibraryAction);
CREATE_ACTION_ICON_SHORTCUT(OpenFileAction, "open-file", gt("open-file"), QKeySequence::Open); CREATE_ACTION_ICON_SHORTCUT(OpenFileAction, "open-file", gt("open-file"), QKeySequence::Open);
@ -379,10 +396,10 @@ void KiwixApp::createAction()
HIDE_ACTION(SavePageAsAction); HIDE_ACTION(SavePageAsAction);
*/ */
CREATE_ACTION_SHORTCUT(SearchArticleAction, gt("search-article"), QKeySequence(Qt::CTRL+Qt::Key_L)); CREATE_ACTION_SHORTCUT(SearchArticleAction, gt("search-article"), QKeySequence(Qt::CTRL | Qt::Key_L));
HIDE_ACTION(SearchArticleAction); HIDE_ACTION(SearchArticleAction);
CREATE_ACTION_SHORTCUT(SearchLibraryAction, gt("search-in-library"), QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_R)); CREATE_ACTION_SHORTCUT(SearchLibraryAction, gt("search-in-library"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_R));
HIDE_ACTION(SearchLibraryAction); HIDE_ACTION(SearchLibraryAction);
CREATE_ACTION(FindInPageAction, gt("find-in-page")); CREATE_ACTION(FindInPageAction, gt("find-in-page"));
@ -400,20 +417,20 @@ void KiwixApp::createAction()
}); });
mpa_actions[ToggleFullscreenAction]->setCheckable(true); mpa_actions[ToggleFullscreenAction]->setCheckable(true);
CREATE_ACTION_SHORTCUT(ToggleTOCAction, gt("table-of-content"), QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_1)); CREATE_ACTION_SHORTCUT(ToggleTOCAction, gt("table-of-content"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_1));
HIDE_ACTION(ToggleTOCAction); HIDE_ACTION(ToggleTOCAction);
CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_2)); CREATE_ACTION_ONOFF_ICON_SHORTCUT(ToggleReadingListAction, "reading-list-active", "reading-list", gt("reading-list"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_2));
CREATE_ACTION_SHORTCUTS(ZoomInAction, gt("zoom-in"), QList<QKeySequence>({QKeySequence::ZoomIn, QKeySequence(Qt::CTRL+Qt::Key_Equal)})); CREATE_ACTION_SHORTCUTS(ZoomInAction, gt("zoom-in"), QList<QKeySequence>({QKeySequence::ZoomIn, QKeySequence(Qt::CTRL | Qt::Key_Equal)}));
CREATE_ACTION_SHORTCUT(ZoomOutAction, gt("zoom-out"), QKeySequence::ZoomOut); CREATE_ACTION_SHORTCUT(ZoomOutAction, gt("zoom-out"), QKeySequence::ZoomOut);
CREATE_ACTION_SHORTCUT(ZoomResetAction, gt("zoom-reset"), QKeySequence(Qt::CTRL+Qt::Key_0)); CREATE_ACTION_SHORTCUT(ZoomResetAction, gt("zoom-reset"), QKeySequence(Qt::CTRL | Qt::Key_0));
CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL + Qt::Key_Tab)); CREATE_ACTION_SHORTCUT(NextTabAction, gt("next-tab"), QKeySequence(Qt::CTRL | Qt::Key_Tab));
CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab)); CREATE_ACTION_SHORTCUT(PreviousTabAction, gt("previous-tab"), QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab));
CREATE_ACTION_SHORTCUT(HelpAction, gt("help"), QKeySequence::HelpContents); CREATE_ACTION_SHORTCUT(HelpAction, gt("help"), QKeySequence::HelpContents);
HIDE_ACTION(HelpAction); HIDE_ACTION(HelpAction);
@ -431,7 +448,7 @@ void KiwixApp::createAction()
CREATE_ACTION_ICON_SHORTCUT(SettingAction, "settings", gt("settings"), QKeySequence(Qt::Key_F12)); CREATE_ACTION_ICON_SHORTCUT(SettingAction, "settings", gt("settings"), QKeySequence(Qt::Key_F12));
CREATE_ACTION_ICON_SHORTCUT(DonateAction, "donate", gt("donate-to-support-kiwix"), QKeySequence(Qt::CTRL+Qt::Key_D)); CREATE_ACTION_ICON_SHORTCUT(DonateAction, "donate", gt("donate-to-support-kiwix"), QKeySequence(Qt::CTRL | Qt::Key_D));
CREATE_ACTION_ICON_SHORTCUT(ExitAction, "exit", gt("exit"), QKeySequence::Quit); CREATE_ACTION_ICON_SHORTCUT(ExitAction, "exit", gt("exit"), QKeySequence::Quit);
} }

View File

@ -13,7 +13,11 @@ KProfile::KProfile(QObject *parent) :
settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true); settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, true);
} }
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void KProfile::startDownload(QWebEngineDownloadItem* download) void KProfile::startDownload(QWebEngineDownloadItem* download)
#else
void KProfile::startDownload(QWebEngineDownloadRequest* download)
#endif
{ {
QString defaultFileName = download->url().fileName(); QString defaultFileName = download->url().fileName();
QString fileName = QFileDialog::getSaveFileName(KiwixApp::instance()->getMainWindow(), QString fileName = QFileDialog::getSaveFileName(KiwixApp::instance()->getMainWindow(),
@ -30,7 +34,11 @@ void KProfile::startDownload(QWebEngineDownloadItem* download)
#else #else
download->setDownloadFileName(fileName); download->setDownloadFileName(fileName);
#endif #endif
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
connect(download, &QWebEngineDownloadItem::finished, this, &KProfile::downloadFinished); connect(download, &QWebEngineDownloadItem::finished, this, &KProfile::downloadFinished);
#else
connect(download, &QWebEngineDownloadRequest::isFinished, this, &KProfile::downloadFinished);
#endif
download->accept(); download->accept();
} }

View File

@ -2,6 +2,11 @@
#define KPROFILE_H #define KPROFILE_H
#include <QWebEngineProfile> #include <QWebEngineProfile>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QWebEngineDownloadItem>
#else
#include <QWebEngineDownloadRequest>
#endif
#include "urlschemehandler.h" #include "urlschemehandler.h"
@ -16,7 +21,12 @@ private:
signals: signals:
public slots: public slots:
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void startDownload(QWebEngineDownloadItem*); void startDownload(QWebEngineDownloadItem*);
#else
void startDownload(QWebEngineDownloadRequest*);
#endif
void downloadFinished(); void downloadFinished();
}; };

View File

@ -18,8 +18,10 @@ int main(int argc, char *argv[])
} }
// End of hack ^^^ // End of hack ^^^
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
// High DPI Scaling is enabled by default in Qt6. This attribute no longer exists in 6.0 and later
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
QWebEngineUrlScheme scheme("zim"); QWebEngineUrlScheme scheme("zim");
QWebEngineUrlScheme::registerScheme(scheme); QWebEngineUrlScheme::registerScheme(scheme);

View File

@ -1,5 +1,6 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "portutils.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "ui_about.h" #include "ui_about.h"
@ -108,10 +109,12 @@ bool MainWindow::eventFilter(QObject* /*object*/, QEvent* event)
{ {
const auto mouseEvent = static_cast<QMouseEvent*>(event); const auto mouseEvent = static_cast<QMouseEvent*>(event);
const int tabRegion = getTabBar()->height() + getTopWidget()->height() + 30; const int tabRegion = getTabBar()->height() + getTopWidget()->height() + 30;
int clickY = portutils::getY(*mouseEvent);
// We don't have to check for visibilty as calling hide() on a hidden widget, or show() on a non-hidden widget is a no-op // We don't have to check for visibilty as calling hide() on a hidden widget, or show() on a non-hidden widget is a no-op
if (mouseEvent->y() == 0) { if (clickY == 0) {
showTabAndTop(); showTabAndTop();
} else if(mouseEvent->y() >= tabRegion) { } else if(clickY >= tabRegion) {
hideTabAndTop(); hideTabAndTop();
} }
return true; return true;

View File

@ -27,7 +27,7 @@ public:
QWidget getMainView(); QWidget getMainView();
protected: protected:
void keyPressEvent(QKeyEvent *event); void keyPressEvent(QKeyEvent *event) override;
bool eventFilter(QObject* object, QEvent* event) override; bool eventFilter(QObject* object, QEvent* event) override;
private slots: private slots:

39
src/portutils.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef PORTUTILS_H
#define PORTUTILS_H
#include <QEvent>
namespace portutils {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // Earlier than Qt6
inline int getX(const QMouseEvent& e) {
return e.x();
}
inline int getY(const QMouseEvent& e) {
return e.y();
}
inline QPoint getGlobalPos(const QMouseEvent& e) {
return e.globalPos();
}
#else // Qt6 and later
inline int getX(const QMouseEvent& e) {
return e.position().x();
}
inline int getY(const QMouseEvent& e) {
return e.position().y();
}
inline QPoint getGlobalPos(const QMouseEvent& e) {
return e.globalPosition().toPoint();
}
#endif
}
#endif // PORTUTILS_H

View File

@ -5,6 +5,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <kiwix/tools.h> #include <kiwix/tools.h>
#include <QLocale> #include <QLocale>
#include <QList>
SettingsManager::SettingsManager(QObject *parent) SettingsManager::SettingsManager(QObject *parent)
: QObject(parent), : QObject(parent),
@ -147,8 +148,18 @@ void SettingsManager::initSettings()
m_kiwixServerIpAddress = m_settings.value("localKiwixServer/ipAddress", QString("0.0.0.0")).toString(); m_kiwixServerIpAddress = m_settings.value("localKiwixServer/ipAddress", QString("0.0.0.0")).toString();
m_monitorDir = m_settings.value("monitor/dir", QString("")).toString(); m_monitorDir = m_settings.value("monitor/dir", QString("")).toString();
m_moveToTrash = m_settings.value("moveToTrash", true).toBool(); m_moveToTrash = m_settings.value("moveToTrash", true).toBool();
QVariant defaultLang = QVariant::fromValue(QLocale::languageToString(QLocale().language()) + '|' + QLocale().name().split("_").at(0)); QString defaultLang = QLocale::languageToString(QLocale().language()) + '|' + QLocale().name().split("_").at(0);
m_langList = m_settings.value("language", {defaultLang}).toList();
QList<QString> defaultLangList; // Qt5 QList doesn't support supplying a constructor list, so use append() for Qt5+Qt6 compat
defaultLangList.append(defaultLang);
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QVariant defaultLangVariant(defaultLangList); // Qt5 requires explicit conversion from QList to QVariant
m_langList = m_settings.value("language", defaultLangVariant).toList();
#else
m_langList = m_settings.value("language", defaultLangList).toList();
#endif
m_categoryList = m_settings.value("category", {}).toList(); m_categoryList = m_settings.value("category", {}).toList();
m_contentTypeList = m_settings.value("contentType", {}).toList(); m_contentTypeList = m_settings.value("contentType", {}).toList();
} }

View File

@ -1,3 +1,5 @@
class QMenu;
#include "tabbar.h" #include "tabbar.h"
#include "kiwixapp.h" #include "kiwixapp.h"
@ -76,7 +78,7 @@ TabBar::TabBar(QWidget *parent) :
for (int i = 0 ; i <= 9 ; i++) { for (int i = 0 ; i <= 9 ; i++) {
QAction *a = new QAction(this); QAction *a = new QAction(this);
a->setData(QVariant::fromValue(i)); a->setData(QVariant::fromValue(i));
QKeySequence ks(Qt::ALT + (Qt::Key_0 + i)); QKeySequence ks(Qt::ALT | (Qt::Key_0 + i));
a->setShortcut(ks); a->setShortcut(ks);
addAction(a); addAction(a);
connect(a, &QAction::triggered, this, [=](){ connect(a, &QAction::triggered, this, [=](){

View File

@ -4,6 +4,7 @@
#include "kiwixapp.h" #include "kiwixapp.h"
#include "mainmenu.h" #include "mainmenu.h"
#include "tabbar.h" #include "tabbar.h"
#include "portutils.h"
#include <QMouseEvent> #include <QMouseEvent>
#include <QAction> #include <QAction>
@ -48,12 +49,13 @@ TopWidget::TopWidget(QWidget *parent) :
addAction(KiwixApp::instance()->getAction(KiwixApp::OpenFileAction)); addAction(KiwixApp::instance()->getAction(KiwixApp::OpenFileAction));
QMenu* menu = new MainMenu(); QMenu* menu = new MainMenu();
QAction* menuAction = new QAction(this); QToolButton *toolButton = new QToolButton(menu);
menuAction->setIcon(QIcon(":/icons/more.svg")); toolButton->setIcon(QIcon(":/icons/more.svg"));
menuAction->setMenu(menu); toolButton->setPopupMode(QToolButton::InstantPopup);
menuAction->setToolTip(gt("main-menu")); toolButton->setToolTip(gt("main-menu"));
toolButton->setMenu(menu);
addWidget(toolButton);
addAction(menuAction);
setContextMenuPolicy( Qt::PreventContextMenu ); setContextMenuPolicy( Qt::PreventContextMenu );
#if !SYSTEMTITLEBAR #if !SYSTEMTITLEBAR
@ -92,7 +94,8 @@ void TopWidget::mousePressEvent(QMouseEvent *event) {
if (event->button() != Qt::LeftButton) if (event->button() != Qt::LeftButton)
return; return;
m_cursorPos = event->globalPos() + frameGeometry().topLeft() - parentWidget()->frameGeometry().topLeft(); QPoint globalPos = portutils::getGlobalPos(*event);
m_cursorPos = globalPos + frameGeometry().topLeft() - parentWidget()->frameGeometry().topLeft();
m_timestamp = event->timestamp(); m_timestamp = event->timestamp();
event->accept(); event->accept();
} }
@ -101,8 +104,9 @@ void TopWidget::mouseMoveEvent(QMouseEvent *event) {
if (event->timestamp() <= m_timestamp) if (event->timestamp() <= m_timestamp)
return; return;
QPoint globalPos = portutils::getGlobalPos(*event);
m_timestamp = event->timestamp(); m_timestamp = event->timestamp();
auto delta = event->globalPos() - m_cursorPos; auto delta = globalPos - m_cursorPos;
parentWidget()->move(delta); parentWidget()->move(delta);
event->accept(); event->accept();
} }

View File

@ -5,6 +5,7 @@
class UrlSchemeHandler : public QWebEngineUrlSchemeHandler class UrlSchemeHandler : public QWebEngineUrlSchemeHandler
{ {
Q_OBJECT
public: public:
UrlSchemeHandler(); UrlSchemeHandler();
void requestStarted(QWebEngineUrlRequestJob *request); void requestStarted(QWebEngineUrlRequestJob *request);

View File

@ -5,6 +5,7 @@
class WebPage : public QWebEnginePage class WebPage : public QWebEnginePage
{ {
Q_OBJECT
public: public:
explicit WebPage(QObject *parent = nullptr); explicit WebPage(QObject *parent = nullptr);

View File

@ -1,3 +1,5 @@
class QMenu;
#include "webview.h" #include "webview.h"
#include <QDesktopServices> #include <QDesktopServices>
@ -80,11 +82,13 @@ WebView::WebView(QWidget *parent)
* If the page is search results, we put the default zoom factor * If the page is search results, we put the default zoom factor
* If in Qt 6.x, the bug is fixed this code can be removed. * If in Qt 6.x, the bug is fixed this code can be removed.
*/ */
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
connect(this, &QWebEngineView::loadFinished, this, [=] (bool ok) { connect(this, &QWebEngineView::loadFinished, this, [=] (bool ok) {
if (ok) { if (ok) {
applyCorrectZoomFactor(); applyCorrectZoomFactor();
} }
}); });
#endif
} }
WebView::~WebView() WebView::~WebView()
@ -202,26 +206,70 @@ void WebView::wheelEvent(QWheelEvent *event) {
void WebView::contextMenuEvent(QContextMenuEvent *event) void WebView::contextMenuEvent(QContextMenuEvent *event)
{ {
auto menu = this->page()->createStandardContextMenu(); QMenu* menu;
pageAction(QWebEnginePage::OpenLinkInNewWindow)->setVisible(false); if (m_linkHovered.isEmpty()) {
if (!m_linkHovered.isEmpty()) { menu = createStandardContextMenu();
if (!m_linkHovered.startsWith("zim://")) { } else {
pageAction(QWebEnginePage::OpenLinkInNewTab)->setVisible(false); menu = createLinkContextMenu();
auto openLinkInWebBrowserAction = new QAction(gt("open-link-in-web-browser"));
menu->insertAction(pageAction(QWebEnginePage::DownloadLinkToDisk) , openLinkInWebBrowserAction);
connect(menu, &QObject::destroyed, openLinkInWebBrowserAction, &QObject::deleteLater);
connect(openLinkInWebBrowserAction, &QAction::triggered, this, [=](bool checked) {
Q_UNUSED(checked);
QDesktopServices::openUrl(m_linkHovered);
});
} else {
pageAction(QWebEnginePage::OpenLinkInNewTab)->setVisible(true);
}
} }
menu->exec(event->globalPos()); menu->exec(event->globalPos());
} }
QMenu* WebView::createStandardContextMenu() {
auto app = KiwixApp::instance();
QMenu* menu = new QMenu(this);
auto backAction = new QAction(gt("back"));
backAction->setEnabled(app->getAction(KiwixApp::HistoryBackAction)->isEnabled());
backAction->setIcon(app->getAction(KiwixApp::HistoryBackAction)->icon());
menu->addAction(backAction);
connect(menu, &QObject::destroyed, backAction, &QObject::deleteLater);
connect(backAction, &QAction::triggered, this, [=](bool checked) {
Q_UNUSED(checked);
KiwixApp::instance()->getTabWidget()->triggerWebPageAction(QWebEnginePage::Back);
});
auto forwardAction = new QAction(gt("forward"));
forwardAction->setEnabled(app->getAction(KiwixApp::HistoryForwardAction)->isEnabled());
forwardAction->setIcon(app->getAction(KiwixApp::HistoryForwardAction)->icon());
menu->addAction(forwardAction);
connect(menu, &QObject::destroyed, forwardAction, &QObject::deleteLater);
connect(forwardAction, &QAction::triggered, this, [=](bool checked) {
Q_UNUSED(checked);
KiwixApp::instance()->getTabWidget()->triggerWebPageAction(QWebEnginePage::Forward);
});
return menu;
}
QMenu* WebView::createLinkContextMenu() {
QMenu* menu = new QMenu(this);
if (!m_linkHovered.startsWith("zim://")) {
auto openLinkInWebBrowserAction = new QAction(gt("open-link-in-web-browser"));
menu->addAction(openLinkInWebBrowserAction);
connect(menu, &QObject::destroyed, openLinkInWebBrowserAction, &QObject::deleteLater);
connect(openLinkInWebBrowserAction, &QAction::triggered, this, [=](bool checked) {
Q_UNUSED(checked);
QDesktopServices::openUrl(m_linkHovered);
});
} else {
auto openLinkNewTab = new QAction(gt("open-link-new-tab"));
openLinkNewTab->setIcon(QIcon(":/icons/new-tab-icon.svg"));
menu->addAction(openLinkNewTab);
connect(menu, &QObject::destroyed, openLinkNewTab, &QObject::deleteLater);
connect(openLinkNewTab, &QAction::triggered, this, [=](bool checked) {
Q_UNUSED(checked);
KiwixApp::instance()->openUrl(m_linkHovered, true);
});
}
return menu;
}
bool WebView::eventFilter(QObject *src, QEvent *e) bool WebView::eventFilter(QObject *src, QEvent *e)
{ {
Q_UNUSED(src) Q_UNUSED(src)

View File

@ -1,10 +1,10 @@
#ifndef WEBVIEW_H #ifndef WEBVIEW_H
#define WEBVIEW_H #define WEBVIEW_H
#include <QMenu>
#include <QWebEngineView> #include <QWebEngineView>
#include <QIcon> #include <QIcon>
#include <QWheelEvent> #include <QWheelEvent>
#include <QMenu>
#include "findinpagebar.h" #include "findinpagebar.h"
@ -69,6 +69,8 @@ private slots:
private: private:
void addHistoryItemAction(QMenu *menu, const QWebEngineHistoryItem &item, int n) const; void addHistoryItemAction(QMenu *menu, const QWebEngineHistoryItem &item, int n) const;
void applyCorrectZoomFactor(); void applyCorrectZoomFactor();
QMenu* createStandardContextMenu();
QMenu* createLinkContextMenu();
}; };
#endif // WEBVIEW_H #endif // WEBVIEW_H

View File

@ -38,11 +38,11 @@
** **
****************************************************************************/ ****************************************************************************/
#include "qtlocalpeer.h" #include "qtlocalpeer.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDataStream> #include <QDataStream>
#include <QTime> #include <QTime>
#include <QRegularExpression>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#include <QLibrary> #include <QLibrary>
@ -76,15 +76,25 @@ QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
id = id.toLower(); id = id.toLower();
#endif #endif
prefix = id.section(QLatin1Char('/'), -1); prefix = id.section(u'/', -1);
} }
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
prefix.remove(QRegExp("[^a-zA-Z]")); prefix.remove(QRegExp("[^a-zA-Z]"));
#else
prefix.remove(QRegularExpression("[^a-zA-Z]"));
#endif
prefix.truncate(6); prefix.truncate(6);
QByteArray idc = id.toUtf8(); QByteArray idc = id.toUtf8();
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
quint16 idNum = qChecksum(idc.constData(), idc.size()); quint16 idNum = qChecksum(idc.constData(), idc.size());
#else
QByteArrayView v = QByteArrayView(idc);
quint16 idNum = qChecksum(v);
#endif
socketName = QLatin1String("qtsingleapp-") + prefix socketName = QLatin1String("qtsingleapp-") + prefix
+ QLatin1Char('-') + QString::number(idNum, 16); + u'-' + QString::number(idNum, 16);
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
if (!pProcessIdToSessionId) { if (!pProcessIdToSessionId) {
@ -97,12 +107,12 @@ QtLocalPeer::QtLocalPeer(QObject* parent, const QString &appId)
socketName += QLatin1Char('-') + QString::number(sessionId, 16); socketName += QLatin1Char('-') + QString::number(sessionId, 16);
} }
#else #else
socketName += QLatin1Char('-') + QString::number(::getuid(), 16); socketName += u'-' + QString::number(::getuid(), 16);
#endif #endif
server = new QLocalServer(this); server = new QLocalServer(this);
QString lockName = QDir(QDir::tempPath()).absolutePath() QString lockName = QDir(QDir::tempPath()).absolutePath()
+ QLatin1Char('/') + socketName + u'/' + socketName
+ QLatin1String("-lockfile"); + QLatin1String("-lockfile");
lockFile.setFileName(lockName); lockFile.setFileName(lockName);
lockFile.open(QIODevice::ReadWrite); lockFile.open(QIODevice::ReadWrite);
@ -122,7 +132,7 @@ bool QtLocalPeer::isClient()
#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0)) #if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(4,5,0))
// ### Workaround // ### Workaround
if (!res && server->serverError() == QAbstractSocket::AddressInUseError) { if (!res && server->serverError() == QAbstractSocket::AddressInUseError) {
QFile::remove(QDir::cleanPath(QDir::tempPath())+QLatin1Char('/')+socketName); QFile::remove(QDir::cleanPath(QDir::tempPath())+u'/'+socketName);
res = server->listen(socketName); res = server->listen(socketName);
} }
#endif #endif

View File

@ -66,6 +66,9 @@ namespace QtLP_Private {
class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile class QT_QTLOCKEDFILE_EXPORT QtLockedFile : public QFile
{ {
//Q_OBJECT
// TODO: Uncomment Q_OBJECT. Setting Q_OBJECT here causes this error:
// undefined reference to `vtable for QtLP_Private::QtLockedFile'
public: public:
enum LockMode { NoLock = 0, ReadLock, WriteLock }; enum LockMode { NoLock = 0, ReadLock, WriteLock };