Commit 7bd0f8f3 authored by Felix Ernst's avatar Felix Ernst Committed by Albert Astals Cid
Browse files

Add KHamburgerMenu

parent f686f4c6
......@@ -33,6 +33,7 @@
#include <QLabel>
#include <QLayout>
#include <QMenu>
#include <QMenuBar>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QPrinter>
......@@ -44,14 +45,19 @@
#include <QTimer>
#include <QWidgetAction>
#include "kconfigwidgets_version.h" // TODO KF 5.81 Remove this include, because the relevant section below will also be removed.
#include <KAboutPluginDialog>
#include <KActionCollection>
#include <KBookmarkAction>
#include <KDirWatch>
#include <KFilterBase>
#include <KFilterDev>
#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0)
#include <KHamburgerMenu>
#endif
#include <KIO/OpenFileManagerWindowJob>
#include <KJobWidgets>
#include <KMainWindow>
#include <KMessageBox>
#include <KParts/GUIActivateEvent>
#include <KPasswordDialog>
......@@ -60,6 +66,7 @@
#include <KStandardShortcut>
#include <KToggleAction>
#include <KToggleFullScreenAction>
#include <KToolBar>
#include <Kdelibs4ConfigMigrator>
#include <Kdelibs4Migration>
#ifdef WITH_KWALLET
......@@ -110,7 +117,9 @@
#include "thumbnaillist.h"
#include "toc.h"
#include "xmlgui_helper.h"
#include <memory>
#include <type_traits>
#ifdef OKULAR_KEEP_FILE_OPEN
class FileKeeper
......@@ -288,7 +297,6 @@ Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList &args)
, m_fileWasRemoved(false)
, m_showMenuBarAction(nullptr)
, m_showFullScreenAction(nullptr)
, m_actionsSearched(false)
, m_cliPresentation(false)
, m_cliPrint(false)
, m_cliPrintAndExit(false)
......@@ -743,6 +751,8 @@ void Part::setupViewerActions()
m_saveAs = nullptr;
m_openContainingFolder = nullptr;
m_hamburgerMenuAction = nullptr;
QAction *prefs = KStandardAction::preferences(this, SLOT(slotPreferences()), ac);
if (m_embedMode == NativeShellMode) {
prefs->setText(i18n("Configure Okular..."));
......@@ -923,6 +933,16 @@ void Part::setupActions()
connect(m_openContainingFolder, &QAction::triggered, this, &Part::slotOpenContainingFolder);
m_openContainingFolder->setEnabled(false);
#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0)
if (m_embedMode == Okular::NativeShellMode) { // This hamburger menu is designed to be quite Okular-specific.
m_hamburgerMenuAction = KStandardAction::hamburgerMenu(nullptr, nullptr, ac);
if (auto *mainWindow = findMainWindow()) {
m_hamburgerMenuAction->setMenuBar(mainWindow->menuBar());
}
connect(m_hamburgerMenuAction, &KHamburgerMenu::aboutToShowMenu, this, &Part::slotUpdateHamburgerMenu);
}
#endif
QAction *importPS = ac->addAction(QStringLiteral("import_ps"));
importPS->setText(i18n("&Import PostScript as PDF..."));
importPS->setIcon(QIcon::fromTheme(QStringLiteral("document-import")));
......@@ -2934,26 +2954,11 @@ void Part::showMenu(const Okular::Page *page, const QPoint point, const QString
bool reallyShow = false;
const bool currentPage = page && page->number() == m_document->viewport().pageNumber;
if (!m_actionsSearched) {
// the quest for options_show_menubar
KActionCollection *ac;
QAction *act;
if (factory()) {
const QList<KXMLGUIClient *> clients(factory()->clients());
for (int i = 0; (!m_showMenuBarAction || !m_showFullScreenAction) && i < clients.size(); ++i) {
ac = clients.at(i)->actionCollection();
// show_menubar
act = ac->action(QStringLiteral("options_show_menubar"));
if (act && qobject_cast<KToggleAction *>(act))
m_showMenuBarAction = qobject_cast<KToggleAction *>(act);
// fullscreen
act = ac->action(QStringLiteral("fullscreen"));
if (act && qobject_cast<KToggleFullScreenAction *>(act))
m_showFullScreenAction = qobject_cast<KToggleFullScreenAction *>(act);
}
}
m_actionsSearched = true;
if (!m_showMenuBarAction) {
m_showMenuBarAction = findActionInKPartHierarchy<KToggleAction>(KStandardAction::name(KStandardAction::ShowMenubar));
}
if (!m_showFullScreenAction) {
m_showFullScreenAction = findActionInKPartHierarchy<KToggleFullScreenAction>(KStandardAction::name(KStandardAction::FullScreen));
}
QMenu *popup = new QMenu(widget());
......@@ -2981,12 +2986,20 @@ void Part::showMenu(const Okular::Page *page, const QPoint point, const QString
reallyShow = true;
}
if ((m_showMenuBarAction && !m_showMenuBarAction->isChecked()) || (m_showFullScreenAction && m_showFullScreenAction->isChecked())) {
popup->addAction(new OKMenuTitle(popup, i18n("Tools")));
if (m_showMenuBarAction && !m_showMenuBarAction->isChecked())
const int amountOfActions = popup->actions().count();
if (m_showMenuBarAction && !m_showMenuBarAction->isChecked()) {
if (m_hamburgerMenuAction) {
#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0)
m_hamburgerMenuAction->addToMenu(popup);
#endif
} else if (m_showMenuBarAction) {
popup->addAction(m_showMenuBarAction);
if (m_showFullScreenAction && m_showFullScreenAction->isChecked())
popup->addAction(m_showFullScreenAction);
}
}
if (m_showFullScreenAction && m_showFullScreenAction->isChecked())
popup->addAction(m_showFullScreenAction);
if (popup->actions().count() > amountOfActions && popup->actions().constLast()->isVisible()) {
popup->insertAction(popup->actions().at(amountOfActions), new OKMenuTitle(popup, i18n("Tools")));
reallyShow = true;
}
......@@ -3013,6 +3026,34 @@ void Part::showMenu(const Okular::Page *page, const QPoint point, const QString
delete popup;
}
template<class Action> Action *Part::findActionInKPartHierarchy(const QString &actionName)
{
static_assert(std::is_base_of<QAction, Action>::value, "Calling this method to find something other than an Action makes no sense.");
if (factory()) {
const QList<KXMLGUIClient *> clients(factory()->clients());
for (auto client : clients) {
if (QAction *act = client->actionCollection()->action(actionName)) {
if (Action *castedAction = qobject_cast<Action *>(act)) {
return castedAction;
}
}
}
}
return nullptr;
}
KMainWindow *Part::findMainWindow()
{
auto *potentialMainWindow = parent();
while (potentialMainWindow) {
if (auto *mainWindow = qobject_cast<KMainWindow *>(potentialMainWindow)) {
return mainWindow;
}
potentialMainWindow = potentialMainWindow->parent();
}
return nullptr;
}
void Part::slotShowProperties()
{
PropertiesDialog *d = new PropertiesDialog(widget(), m_document);
......@@ -3040,6 +3081,116 @@ void Part::slotHidePresentation()
delete (PresentationWidget *)m_presentationWidget;
}
void Part::slotUpdateHamburgerMenu()
{
#if KCONFIGWIDGETS_VERSION >= QT_VERSION_CHECK(5, 81, 0)
auto ac = actionCollection();
auto menu = m_hamburgerMenuAction->menu();
if (!menu) {
menu = new QMenu(widget());
m_hamburgerMenuAction->setMenu(menu);
if (!m_showMenuBarAction) {
m_showMenuBarAction = findActionInKPartHierarchy<KToggleAction>(KStandardAction::name(KStandardAction::ShowMenubar));
}
m_hamburgerMenuAction->setShowMenuBarAction(m_showMenuBarAction);
} else {
menu->clear();
}
QToolBar *visibleMainToolbar = nullptr;
if (auto *mainWindow = findMainWindow()) {
visibleMainToolbar = mainWindow->toolBar();
if (!visibleMainToolbar->isVisible()) {
visibleMainToolbar = nullptr;
}
const auto toolbars = mainWindow->toolBars();
for (const auto &toolbar : toolbars) {
m_hamburgerMenuAction->hideActionsOf(toolbar);
}
bool menuAvailable = false; // We already know the menu bar is hidden when this menu is opened.
// If no menu is available, we want to add actions to the hamburger menu to show them again.
// The hamburger menu serves as the fallback that is available through the right-click context menu.
if (visibleMainToolbar && visibleMainToolbar->actions().contains(m_hamburgerMenuAction)) {
menuAvailable = true;
}
if (!menuAvailable) {
menu->addAction(m_showMenuBarAction);
if (!visibleMainToolbar) {
menu->addAction(findActionInKPartHierarchy(QStringLiteral("mainToolBar")));
}
menu->addSeparator();
}
}
// When changing actions, keep "Simple by default, powerful when needed" in mind.
// To retrieve an action, it is fastest to use a direct pointer if available (m_action), otherwise use
// ac->action(actionName) and if the action isn't in the actionCollection() of this part,
// use findActionInKPartHierarchy(actionName).
menu->addAction(findActionInKPartHierarchy(KStandardAction::name(KStandardAction::Open)));
menu->addAction(findActionInKPartHierarchy(KStandardAction::name(KStandardAction::OpenRecent)));
menu->addAction(m_save);
menu->addAction(m_saveAs);
menu->addSeparator();
menu->addAction(ac->action(QStringLiteral("mouse_drag")));
if (!visibleMainToolbar || (visibleMainToolbar && !visibleMainToolbar->actions().contains(ac->action(QStringLiteral("mouse_selecttools"))))) {
menu->addAction(ac->action(QStringLiteral("mouse_select")));
}
menu->addAction(m_copy);
menu->addAction(m_find);
menu->addAction(m_showLeftPanel);
if (!visibleMainToolbar || (visibleMainToolbar && !visibleMainToolbar->actions().contains(ac->action(QStringLiteral("annotation_favorites"))))) {
menu->addAction(ac->action(QStringLiteral("mouse_toggle_annotate")));
}
menu->addAction(ac->action(KStandardAction::name(KStandardAction::Undo)));
menu->addAction(ac->action(KStandardAction::name(KStandardAction::Redo)));
menu->addSeparator();
menu->addAction(findActionInKPartHierarchy(KStandardAction::name(KStandardAction::Print)));
menu->addAction(m_printPreview);
menu->addAction(m_showProperties);
menu->addAction(m_openContainingFolder);
menu->addAction(m_share);
menu->addSeparator();
menu->addAction(ac->action(QStringLiteral("zoom_to")));
const QMenuBar *menuBar = m_hamburgerMenuAction->menuBar();
if (menuBar && menuBar->actions().count() < 3) { // 3 to make sure none of the code below can crash.
menuBar = nullptr;
}
auto curatedViewMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("page-2sides")), menuBar ? menuBar->actions().at(1)->text() : QStringLiteral("View"));
if (!m_showFullScreenAction) {
m_showFullScreenAction = findActionInKPartHierarchy<KToggleFullScreenAction>(KStandardAction::name(KStandardAction::FullScreen));
}
curatedViewMenu->addAction(m_showFullScreenAction);
curatedViewMenu->addAction(m_showPresentation);
curatedViewMenu->addSeparator();
curatedViewMenu->addAction(findActionInKPartHierarchy(QStringLiteral("view_render_mode")));
if (auto *viewOrientationMenu = qobject_cast<QMenu *>(factory()->container(QStringLiteral("view_orientation"), this))) {
curatedViewMenu->addAction(viewOrientationMenu->menuAction());
}
curatedViewMenu->addAction(findActionInKPartHierarchy(QStringLiteral("view_trim_mode")));
curatedViewMenu->addSeparator();
curatedViewMenu->addAction(ac->action(QStringLiteral("view_toggle_forms")));
m_hamburgerMenuAction->hideActionsOf(curatedViewMenu);
#ifdef HAVE_SPEECH
auto speakMenu = menu->addMenu(QIcon::fromTheme(QStringLiteral("text-speak")), i18nc("@action:inmenu menu that contains actions to control text to speach", "Speak"));
speakMenu->addAction(ac->action(QStringLiteral("speak_document")));
speakMenu->addAction(ac->action(QStringLiteral("speak_current_page")));
speakMenu->addAction(ac->action(QStringLiteral("speak_stop_all")));
speakMenu->addAction(ac->action(QStringLiteral("speak_pause_resume")));
m_hamburgerMenuAction->hideActionsOf(speakMenu);
#endif
// Add the "Settings" menu from the menu bar.
if (menuBar) {
menu->addAction(menuBar->actions().at(menuBar->actions().count() - 3));
}
#endif
}
void Part::slotTogglePresentation()
{
if (m_document->isOpened()) {
......
......@@ -44,6 +44,8 @@ class QMenu;
class KConfigDialog;
class KDirWatch;
class KHamburgerMenu;
class KMainWindow;
class KToggleAction;
class KToggleFullScreenAction;
class QTemporaryFile;
......@@ -219,6 +221,14 @@ protected Q_SLOTS:
void slotShowBottomBar();
void slotShowPresentation();
void slotHidePresentation();
/**
* Updates the menu that is by default at the right end of the toolbar.
* In true "simple by default" fashion, the menu only contains the most important actions
* which are needed to use all essential Okular features. More advanced actions can be
* discovered through a sub-menu (@see KConfigWidgets::KHamburgerMenu::setMenuBarAdvertised()).
*/
void slotUpdateHamburgerMenu();
void slotExportAs(QAction *);
bool slotImportPSFile();
void slotAboutBackend();
......@@ -257,6 +267,14 @@ public Q_SLOTS:
private:
bool aboutToShowContextMenu(QMenu *menu, QAction *action, QMenu *contextMenu);
void showMenu(const Okular::Page *page, const QPoint point, const QString &bookmarkTitle = QString(), const Okular::DocumentViewport &vp = DocumentViewport(), bool showTOCActions = false);
/**
* Searches the actionCollections of all KXMLGUIClients that were created by the same factory()
* as this Part for a QAction that has both the specified name and the specified class.
* @return an action with class @p Action and name @p actionName. nullptr if no such action is found.
*/
template<class Action = QAction> Action *findActionInKPartHierarchy(const QString &actionName);
/** @return the first KMainWindow among the ancestors of this part. nullptr if no KMainWindow is found. */
KMainWindow *findMainWindow();
bool eventFilter(QObject *watched, QEvent *event) override;
Document::OpenResult doOpenFile(const QMimeType &mime, const QString &fileNameToOpen, bool *isCompressedFile);
bool openUrl(const QUrl &url, bool swapInsteadOfOpening);
......@@ -387,6 +405,7 @@ private:
#endif
QAction *m_showPresentation;
QAction *m_openContainingFolder;
KHamburgerMenu *m_hamburgerMenuAction;
KToggleAction *m_showMenuBarAction;
KToggleAction *m_showLeftPanel;
KToggleAction *m_showBottomBar;
......@@ -401,7 +420,6 @@ private:
QAction *m_closeFindBar;
DrawingToolActions *m_presentationDrawingActions;
bool m_actionsSearched;
BrowserExtension *m_bExtension;
QList<Okular::ExportFormat> m_exportFormats;
......
<?xml version="1.0"?>
<!DOCTYPE gui SYSTEM "kpartgui.dtd">
<gui name="okular_part" version="50">
<gui name="okular_part" version="51">
<MenuBar>
<Menu name="file"><text>&amp;File</text>
<Action name="get_new_stuff" group="file_open"/>
......@@ -109,6 +109,8 @@
<Action name="view_zoom_out"/>
<Action name="zoom_to" />
<Action name="view_zoom_in"/>
<Separator/>
<Action name="hamburger_menu"/>
</ToolBar>
<ActionProperties scheme="Default">
<Action priority="0" name="show_leftpanel"/>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment