File 2001-new-welcomeview.patch of Package okular

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a0bb9a545cf61367c5e2f2d39d41e698a065f993..c852dad587e46d98d8d820a900538d524e8d2370 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -412,6 +412,7 @@ install( FILES
            interfaces/printinterface.h
            interfaces/saveinterface.h
            interfaces/viewerinterface.h
+           interfaces/bookmarksinterface.h
          DESTINATION ${KDE_INSTALL_INCLUDEDIR}/okular/interfaces COMPONENT Devel)
 
 ki18n_wrap_ui(okularcore_SRCS
diff --git a/aboutdata.h b/aboutdata.h
index 90cfa95328370528130b8d1d163d8a5f9b5c185c..aae106d60d7078a27eeff99577e8aada9fd3dd15 100644
--- a/aboutdata.h
+++ b/aboutdata.h
@@ -13,6 +13,8 @@
 
 #include <KLocalizedString>
 
+#include <QIcon>
+
 inline KAboutData okularAboutData()
 {
     KAboutData about(QStringLiteral("okular"),
@@ -28,6 +30,7 @@ inline KAboutData okularAboutData()
                      QString(),
                      QStringLiteral("https://okular.kde.org"));
 
+    about.setProgramLogo(QIcon(QStringLiteral(":/kxmlgui5/okular/okular.svgz")));
     about.addAuthor(i18n("Pino Toscano"), i18n("Former maintainer"), QStringLiteral("pino@kde.org"));
     about.addAuthor(i18n("Tobias Koenig"), i18n("Lots of framework work, FictionBook backend and former ODT backend"), QStringLiteral("tokoe@kde.org"));
     about.addAuthor(i18n("Albert Astals Cid"), i18n("Developer"), QStringLiteral("aacid@kde.org"));
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt
index d0d63678ce22e44106bc0d0024eb36916090c7c4..51752be7ed1e4eaa6d0b05e3dc736ae1038e055e 100644
--- a/autotests/CMakeLists.txt
+++ b/autotests/CMakeLists.txt
@@ -119,7 +119,7 @@ ecm_add_test(check_distinguished_name_parser.cpp
     LINK_LIBRARIES Qt6::Test)
 
 if(PlasmaActivities_FOUND AND BUILD_DESKTOP)
-    ecm_add_test(mainshelltest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomescreen.cpp ../shell/recentitemsmodel.cpp closedialoghelper.cpp
+    ecm_add_test(mainshelltest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomeview/welcomeview.cpp ../shell/welcomeview/recentfilesmodel.cpp ../shell/welcomeview/bookmarksmodel.cpp closedialoghelper.cpp
         TEST_NAME "mainshelltest"
         LINK_LIBRARIES Qt6::Test Plasma::Activities okularpart okularcore
     )
@@ -131,7 +131,7 @@ if(PlasmaActivities_FOUND AND BUILD_DESKTOP)
 endif()
 
 if(BUILD_DESKTOP)
-    ecm_add_test(annotationtoolbartest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomescreen.cpp ../shell/recentitemsmodel.cpp closedialoghelper.cpp ../shell/welcomescreen.ui
+    ecm_add_test(annotationtoolbartest.cpp ../shell/okular_main.cpp ../shell/shellutils.cpp ../shell/shell.cpp ../shell/welcomeview/welcomeview.cpp ../shell/welcomeview/recentfilesmodel.cpp ../shell/welcomeview/bookmarksmodel.cpp closedialoghelper.cpp ../shell/welcomeview/welcomeview.ui
         TEST_NAME "annotationtoolbartest"
         LINK_LIBRARIES Qt6::Test okularpart
     )
diff --git a/interfaces/bookmarksinterface.h b/interfaces/bookmarksinterface.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ec5b380e63380d33e996e2b9118b8abf52e56b0
--- /dev/null
+++ b/interfaces/bookmarksinterface.h
@@ -0,0 +1,62 @@
+/*
+ *  SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef _OKULAR_BOOKMARKSINTERFACE_H_
+#define _OKULAR_BOOKMARKSINTERFACE_H_
+
+#include "../core/okularcore_export.h"
+
+#include <QUrl>
+
+#ifdef Q_OS_WIN
+#define BOOKMARKSINTERFACE_EXPORT __declspec(dllexport)
+#else
+#define BOOKMARKSINTERFACE_EXPORT OKULARCORE_EXPORT
+#endif
+
+namespace Okular
+{
+/**
+ * @short Abstract interface for managing bookmarks
+ *
+ * This interface can be used to list and open bookmarks.
+ */
+class BOOKMARKSINTERFACE_EXPORT BookmarksInterface
+{
+public:
+    struct Bookmark {
+        QString title;
+        QUrl url;
+    };
+
+    BookmarksInterface() = default;
+    virtual ~BookmarksInterface() = default;
+
+    BookmarksInterface(const BookmarksInterface &) = delete;
+    BookmarksInterface &operator=(const BookmarksInterface &) = delete;
+
+    /**
+     * Returns bookmarks.
+     */
+    virtual QMultiMap<QUrl, Bookmark> getBookmarks() const = 0;
+
+    /**
+     * Open bookmark.
+     */
+    virtual void openBookmark(const QUrl &bookmark) = 0;
+
+    // SIGNALS
+
+    /**
+     * This signal is emitted whenever the bookmark list has changed.
+     */
+    void bookmarksChanged();
+};
+}
+
+Q_DECLARE_INTERFACE(Okular::BookmarksInterface, "org.kde.okular.BookmarksInterface/0.1")
+
+#endif // _OKULAR_BOOKMARKSINTERFACE_H_
diff --git a/part/part.cpp b/part/part.cpp
index 73f56dc55a5945c6beeb196ded3e4896ad392ee2..ae55eda1282b05ac1ac6e6ea24dd597d4b205370 100644
--- a/part/part.cpp
+++ b/part/part.cpp
@@ -359,6 +359,8 @@ Part::Part(QObject *parent, const QVariantList &args)
         setWindowTitleFromDocument();
     });
 
+    connect(m_document->bookmarkManager(), &Okular::BookmarkManager::bookmarksChanged, this, &Part::bookmarksChanged);
+
     if (parent && parent->metaObject()->indexOfSlot(QMetaObject::normalizedSignature("slotQuit()").constData()) != -1) {
         connect(m_document, SIGNAL(quit()), parent, SLOT(slotQuit())); // clazy:exclude=old-style-connect
     } else {
@@ -468,8 +470,6 @@ Part::Part(QObject *parent, const QVariantList &args)
     rightLayout->addWidget(m_signatureMessage);
     m_pageView = new PageView(rightContainer, m_document);
     rightContainer->setFocusProxy(m_pageView);
-    QMetaObject::invokeMethod(m_pageView, "setFocus", Qt::QueuedConnection); // usability setting
-    //    m_splitter->setFocusProxy(m_pageView);
     connect(m_pageView.data(), &PageView::rightClick, this, &Part::slotShowMenu);
     connect(m_pageView, &PageView::triggerSearch, this, [this](const QString &searchText) {
         m_findBar->startSearch(searchText);
@@ -1095,6 +1095,26 @@ void Part::setModified(bool modified)
     }
 }
 
+QMultiMap<QUrl, BookmarksInterface::Bookmark> Part::getBookmarks() const
+{
+    QMultiMap<QUrl, BookmarksInterface::Bookmark> bookmarks;
+
+    const QList<QUrl> files = m_document->bookmarkManager()->files();
+    for (const QUrl &file : files) {
+        const KBookmark::List &bookmarkList = m_document->bookmarkManager()->bookmarks(file);
+        for (const KBookmark &bookmark : bookmarkList) {
+            bookmarks.insert(file, {bookmark.text(), bookmark.url()});
+        }
+    }
+
+    return bookmarks;
+}
+
+void Part::openBookmark(const QUrl &bookmark)
+{
+    openUrlFromBookmarks(bookmark);
+}
+
 void Part::slotHandleActivatedSourceReference(const QString &absFileName, int line, int col, bool *handled)
 {
     Q_EMIT openSourceReference(absFileName, line, col);
diff --git a/part/part.h b/part/part.h
index e54b01b0d4ab5cdb0561b30b3a1ba2e9bf14be08..f3f1733bbe985002b4c2663f32175b313afa9544 100644
--- a/part/part.h
+++ b/part/part.h
@@ -26,7 +26,6 @@
 #include <QList>
 #include <QPointer>
 #include <QProcess>
-#include <QUrl>
 
 #include <KCompressionDevice>
 #include <KIO/Job>
@@ -36,6 +35,7 @@
 
 #include "../core/document.h"
 #include "../core/observer.h"
+#include "../interfaces/bookmarksinterface.h"
 #include "../interfaces/viewerinterface.h"
 #include "../kdocumentviewer.h"
 
@@ -110,12 +110,13 @@ enum EmbedMode {
  * @author Wilco Greven <greven@kde.org>
  * @version 0.2
  */
-class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::DocumentObserver, public KDocumentViewer, public Okular::ViewerInterface
+class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::DocumentObserver, public KDocumentViewer, public Okular::ViewerInterface, public Okular::BookmarksInterface
 {
     Q_OBJECT
     Q_CLASSINFO("D-Bus Interface", "org.kde.okular")
     Q_INTERFACES(KDocumentViewer)
     Q_INTERFACES(Okular::ViewerInterface)
+    Q_INTERFACES(Okular::BookmarksInterface)
 
     friend class PartTest;
 
@@ -161,6 +162,10 @@ public:
      */
     QAbstractItemModel *annotationsModel() const;
 
+    // Okular::BookmarksInterface
+    QMultiMap<QUrl, BookmarksInterface::Bookmark> getBookmarks() const override;
+    void openBookmark(const QUrl &bookmark) override;
+
 public Q_SLOTS: // dbus
     Q_SCRIPTABLE Q_NOREPLY void goToPage(uint page) override;
     Q_SCRIPTABLE Q_NOREPLY void openDocument(const QString &doc);
@@ -200,6 +205,9 @@ Q_SIGNALS:
      */
     void requestOpenNewlySignedFile(const QString &path, int pageNumber);
 
+    // Okular::BookmarksInterface
+    void bookmarksChanged();
+
 protected:
     // reimplemented from KParts::ReadWritePart
     bool openFile() override;
diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt
index c481000c313ea6de9503c808ba7ba6fa6f58147a..e5b9682259ef23f4cc1c8877b5741ab295a95406 100644
--- a/shell/CMakeLists.txt
+++ b/shell/CMakeLists.txt
@@ -11,14 +11,15 @@ set(okular_SRCS
    okular_main.cpp
    shell.cpp
    shellutils.cpp
-   recentitemsmodel.cpp
-   welcomescreen.cpp
+   welcomeview/welcomeview.cpp
+   welcomeview/recentfilesmodel.cpp
+   welcomeview/bookmarksmodel.cpp
 
    shell.qrc
 )
 
 ki18n_wrap_ui(okular_SRCS
-    welcomescreen.ui)
+    welcomeview/welcomeview.ui)
 
 file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/../icons/*-apps-okular.png")
 ecm_add_app_icon(okular_SRCS ICONS ${ICONS_SRCS})
diff --git a/shell/recentitemsmodel.cpp b/shell/recentitemsmodel.cpp
deleted file mode 100644
index 7f4d15a4423494e40ee52445c7c6fbb820984c50..0000000000000000000000000000000000000000
--- a/shell/recentitemsmodel.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
-    SPDX-FileCopyrightText: 2021 Jiří Wolker <woljiri@gmail.com>
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include "recentitemsmodel.h"
-
-#include <QFile>
-#include <QFileIconProvider>
-#include <QFileInfo>
-#include <QIcon>
-
-#include <KConfigGroup>
-
-RecentItemsModel::RecentItemsModel()
-{
-}
-
-RecentItemsModel::~RecentItemsModel()
-{
-}
-
-void RecentItemsModel::loadEntries(const KConfigGroup &cg)
-{
-    clearEntries();
-
-    // Based on implementation of KRecentFilesAction::loadEntries.
-
-    // read file list
-    for (int i = 1; i <= maxItems(); i++) {
-        const QString key = QStringLiteral("File%1").arg(i);
-        const QString value = cg.readPathEntry(key, QString());
-        if (value.isEmpty()) {
-            continue;
-        }
-        const QUrl url = QUrl::fromUserInput(value);
-
-        // Don't restore if file doesn't exist anymore
-        if (url.isLocalFile() && !QFile::exists(url.toLocalFile())) {
-            continue;
-        }
-
-        const QString nameKey = QStringLiteral("Name%1").arg(i);
-        const QString nameValue = cg.readPathEntry(nameKey, url.fileName());
-        m_recentItems.append(RecentItem {nameValue, url});
-    }
-
-    Q_EMIT layoutChanged();
-}
-
-void RecentItemsModel::clearEntries()
-{
-    m_recentItems.clear();
-
-    Q_EMIT layoutChanged();
-}
-
-int RecentItemsModel::rowCount(const QModelIndex &parent) const
-{
-    Q_UNUSED(parent);
-
-    return m_recentItems.size();
-}
-
-QVariant RecentItemsModel::data(const QModelIndex &index, int role) const
-{
-    const RecentItemsModel::RecentItem *item = getItem(index);
-
-    if (item != nullptr) {
-        switch (role) {
-        case Qt::ItemDataRole::DisplayRole:
-            if (item->name.isEmpty()) {
-                if (item->url.isLocalFile()) {
-                    return item->url.toLocalFile();
-                } else {
-                    return item->url.toString();
-                }
-            } else {
-                return item->name;
-            }
-
-        case Qt::ItemDataRole::ToolTipRole:
-            if (item->url.isLocalFile()) {
-                return item->url.toLocalFile();
-            } else {
-                return item->url.toString();
-            }
-
-        case Qt::ItemDataRole::DecorationRole:
-            if (item->url.isLocalFile()) {
-                return m_iconProvider.icon(QFileInfo(item->url.toLocalFile()));
-            } else {
-                // Fallback icon for remote files.
-                return QIcon::fromTheme(QStringLiteral("network-server"));
-            }
-
-        default:
-            return QVariant();
-        }
-    } else {
-        return QVariant();
-    }
-}
-
-RecentItemsModel::RecentItem const *RecentItemsModel::getItem(int index) const
-{
-    if (m_recentItems.size() > index && index >= 0) {
-        // We reverse the order of items.
-        return &m_recentItems[m_recentItems.size() - index - 1];
-    } else {
-        return nullptr;
-    }
-}
-
-RecentItemsModel::RecentItem const *RecentItemsModel::getItem(const QModelIndex &index) const
-{
-    return getItem(index.row());
-}
diff --git a/shell/recentitemsmodel.h b/shell/recentitemsmodel.h
deleted file mode 100644
index 0725ca62943170195cec6a8bf79226a36184665a..0000000000000000000000000000000000000000
--- a/shell/recentitemsmodel.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-    SPDX-FileCopyrightText: 2021 Jiří Wolker <woljiri@gmail.com>
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#ifndef RECENTITEMSMODEL_H
-#define RECENTITEMSMODEL_H
-
-#include <QAbstractListModel>
-#include <QFileIconProvider>
-#include <QList>
-#include <QModelIndex>
-#include <QString>
-#include <QUrl>
-
-class KConfigGroup;
-
-/**
- * @todo write docs
- */
-class RecentItemsModel : public QAbstractListModel
-{
-    Q_OBJECT
-
-public:
-    struct RecentItem {
-        QString name;
-        QUrl url;
-    };
-
-    /**
-     * Default constructor
-     */
-    RecentItemsModel();
-
-    /**
-     * Destructor
-     */
-    ~RecentItemsModel() override;
-
-    void loadEntries(const KConfigGroup &cg);
-    void clearEntries();
-
-    int maxItems() const
-    {
-        return m_maxItems;
-    }
-
-    RecentItemsModel::RecentItem const *getItem(const QModelIndex &) const;
-    RecentItemsModel::RecentItem const *getItem(int index) const;
-
-    // Model implementation:
-    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
-    QVariant data(const QModelIndex &index, int role = Qt::ItemDataRole::DisplayRole) const override;
-
-private:
-    QList<RecentItemsModel::RecentItem> m_recentItems;
-
-    int m_maxItems = 20;
-    QFileIconProvider m_iconProvider;
-};
-
-#endif // RECENTITEMSMODEL_H
diff --git a/shell/shell.cpp b/shell/shell.cpp
index 0ff9efebd2618f64bb616b54b13715e6ba190dee..f69fe6384e95f63326e83b349f40a1d24a331b8d 100644
--- a/shell/shell.cpp
+++ b/shell/shell.cpp
@@ -61,9 +61,11 @@
 #include <kxmlgui_version.h>
 
 // local includes
+#include "../interfaces/bookmarksinterface.h"
 #include "../interfaces/viewerinterface.h"
 #include "kdocumentviewer.h"
 #include "shellutils.h"
+#include "welcomeview/welcomeview.h"
 
 static const char *shouldShowMenuBarComingFromFullScreen = "shouldShowMenuBarComingFromFullScreen";
 static const char *shouldShowToolBarComingFromFullScreen = "shouldShowToolBarComingFromFullScreen";
@@ -220,16 +222,24 @@ Shell::Shell(const QString &serializedOptions)
         m_centralStackedWidget = new ResizableStackedWidget();
         setCentralWidget(m_centralStackedWidget);
 
+        m_sidebar = new Sidebar;
+        m_sidebar->setObjectName(QStringLiteral("okular_sidebar"));
+        m_sidebar->setContextMenuPolicy(Qt::ActionsContextMenu);
+        m_sidebar->setWindowTitle(i18n("Sidebar"));
+        connect(m_sidebar, &QDockWidget::visibilityChanged, this, [this](bool visible) {
+            // sync sidebar visibility with the m_showSidebarAction only if welcome screen is hidden
+            if (m_showSidebarAction && m_centralStackedWidget->currentWidget() != m_welcomeScreen) {
+                m_showSidebarAction->setChecked(visible);
+            }
+        });
+        addDockWidget(Qt::LeftDockWidgetArea, m_sidebar);
+
+        setupActions();
+
         // Setup the welcome screen
-        m_welcomeScreen = new WelcomeScreen(this);
-        connect(m_welcomeScreen, &WelcomeScreen::openClicked, this, &Shell::fileOpen);
-        connect(m_welcomeScreen, &WelcomeScreen::closeClicked, this, &Shell::hideWelcomeScreen);
-        connect(m_welcomeScreen, &WelcomeScreen::recentItemClicked, this, [this](const QUrl &url) { openUrl(url); });
-        connect(m_welcomeScreen, &WelcomeScreen::forgetRecentItem, this, &Shell::forgetRecentItem);
+        m_welcomeScreen = new WelcomeView(this, firstPart);
         m_centralStackedWidget->addWidget(m_welcomeScreen);
 
-        m_welcomeScreen->installEventFilter(this);
-
         // Setup tab bar
         m_tabWidget = new QTabWidget(this);
         m_tabWidget->setTabsClosable(true);
@@ -239,34 +249,17 @@ Shell::Shell(const QString &serializedOptions)
         m_tabWidget->setMovable(true);
 
         m_tabWidget->setAcceptDrops(true);
+
+        m_welcomeScreen->installEventFilter(this);
         m_tabWidget->tabBar()->installEventFilter(this);
 
         m_centralStackedWidget->addWidget(m_tabWidget);
+        m_centralStackedWidget->setCurrentWidget(m_tabWidget);
 
         connect(m_tabWidget, &QTabWidget::currentChanged, this, &Shell::setActiveTab);
         connect(m_tabWidget, &QTabWidget::tabCloseRequested, this, &Shell::closeTab);
         connect(m_tabWidget->tabBar(), &QTabBar::tabMoved, this, &Shell::moveTabData);
 
-        m_sidebar = new Sidebar;
-        m_sidebar->setObjectName(QStringLiteral("okular_sidebar"));
-        m_sidebar->setContextMenuPolicy(Qt::ActionsContextMenu);
-        m_sidebar->setWindowTitle(i18n("Sidebar"));
-        connect(m_sidebar, &QDockWidget::visibilityChanged, this, [this](bool visible) {
-            // sync sidebar visibility with the m_showSidebarAction only if welcome screen is hidden
-            if (m_showSidebarAction && m_centralStackedWidget->currentWidget() != m_welcomeScreen) {
-                m_showSidebarAction->setChecked(visible);
-            }
-            if (m_centralStackedWidget->currentWidget() == m_welcomeScreen) {
-                // MainWindow tries hard to make its child dockwidgets shown, but during
-                // welcome screen we don't want to see the sidebar,
-                // so try a bit more to actually hide it.
-                m_sidebar->hide();
-            }
-        });
-        addDockWidget(Qt::LeftDockWidgetArea, m_sidebar);
-
-        // then, setup our actions
-        setupActions();
         connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
         // and integrate the part's GUI with the shell's
         setupGUI(Keys | ToolBar | Save);
@@ -411,6 +404,11 @@ bool Shell::openDocument(const QUrl &url, const QString &serializedOptions)
     return true;
 }
 
+KRecentFilesAction *Shell::recentFilesAction() const
+{
+    return m_recent;
+}
+
 bool Shell::openDocument(const QString &urlString, const QString &serializedOptions)
 {
     return openDocument(QUrl(urlString), serializedOptions);
@@ -495,6 +493,18 @@ void Shell::openUrl(const QUrl &url, const QString &serializedOptions)
     }
 }
 
+void Shell::openBookmark(const QUrl &url)
+{
+    hideWelcomeScreen();
+
+    const int activeTab = m_tabWidget->currentIndex();
+    if (activeTab >= 0 && activeTab < m_tabs.size()) {
+        auto activePart = qobject_cast<Okular::BookmarksInterface *>(m_tabs[activeTab].part);
+        Q_ASSERT(activePart);
+        activePart->openBookmark(url);
+    }
+}
+
 void Shell::closeUrl()
 {
     closeTab(m_tabWidget->currentIndex());
@@ -564,8 +574,6 @@ void Shell::setupActions()
     m_recent = KStandardAction::openRecent(this, SLOT(openUrl(QUrl)), actionCollection());
     m_recent->setToolBarMode(KRecentFilesAction::MenuMode);
     connect(m_recent, &QAction::triggered, this, &Shell::showOpenRecentMenu);
-    connect(m_recent, &KRecentFilesAction::recentListCleared, this, &Shell::refreshRecentsOnWelcomeScreen);
-    connect(m_welcomeScreen, &WelcomeScreen::forgetAllRecents, m_recent, &KRecentFilesAction::clear);
     m_recent->setToolTip(i18n("Click to open a file\nClick and hold to open a recent file"));
     m_recent->setWhatsThis(i18n("<b>Click</b> to open a file or <b>Click and hold</b> to select a recent file"));
     m_printAction = KStandardAction::print(this, SLOT(print()), actionCollection());
@@ -604,6 +612,12 @@ void Shell::setupActions()
     m_lockSidebarAction->setText(i18n("Lock Sidebar"));
     connect(m_lockSidebarAction, &QAction::triggered, m_sidebar, &Sidebar::setLocked);
     m_sidebar->addAction(m_lockSidebarAction);
+
+    QAction *showWelcomeScreenAction = actionCollection()->addAction(QStringLiteral("help_welcome_page"));
+    showWelcomeScreenAction->setText(i18n("Welcome Page"));
+    showWelcomeScreenAction->setIcon(qApp->windowIcon());
+    showWelcomeScreenAction->setWhatsThis(i18n("Show the welcome page"));
+    connect(showWelcomeScreenAction, &QAction::triggered, this, [this]() { showWelcomeScreen(true); });
 }
 
 void Shell::saveProperties(KConfigGroup &group)
@@ -888,6 +902,8 @@ void Shell::setActiveTab(int tab)
     }
     m_sidebar->setCurrentWidget(sideContainer);
 
+    m_tabs[tab].part->widget()->setFocus();
+
     m_showSidebarAction = m_tabs[tab].part->actionCollection()->action(QStringLiteral("show_leftpanel"));
     Q_ASSERT(m_showSidebarAction);
     m_showSidebarAction->disconnect();
@@ -1135,28 +1151,19 @@ void Shell::hideWelcomeScreen()
     m_showSidebarAction->setEnabled(true);
 }
 
-void Shell::showWelcomeScreen()
+void Shell::showWelcomeScreen(bool force)
 {
+    if (!force) {
+        const KConfigGroup configGroup = KSharedConfig::openConfig()->group(QStringLiteral("General"));
+        if (!configGroup.readEntry("ShowWelcomeScreenOnStartup", true)) {
+            return;
+        }
+    }
+
     m_showSidebarAction->setEnabled(false);
     m_centralStackedWidget->setCurrentWidget(m_welcomeScreen);
+    m_welcomeScreen->findChild<QPushButton *>()->setFocus();
     m_sidebar->setVisible(false);
-
-    refreshRecentsOnWelcomeScreen();
-}
-
-void Shell::refreshRecentsOnWelcomeScreen()
-{
-    saveRecents();
-    m_welcomeScreen->loadRecents();
-}
-
-void Shell::forgetRecentItem(QUrl const &url)
-{
-    if (m_recent != nullptr) {
-        m_recent->removeUrl(url);
-        saveRecents();
-        refreshRecentsOnWelcomeScreen();
-    }
 }
 
 #include "shell.moc"
diff --git a/shell/shell.h b/shell/shell.h
index 4e7738857ffb847d01363e12debeaef3ec05ecbc..020268fa8d386ce1d19bdd7ccdff3b70b38d497d 100644
--- a/shell/shell.h
+++ b/shell/shell.h
@@ -27,9 +27,8 @@
 #endif
 #include <QStackedWidget>
 
-#include "welcomescreen.h"
-
 class Sidebar;
+class WelcomeView;
 class KRecentFilesAction;
 class KToggleAction;
 class QTabWidget;
@@ -70,6 +69,7 @@ public:
     bool isValid() const;
 
     bool openDocument(const QUrl &url, const QString &serializedOptions);
+    KRecentFilesAction *recentFilesAction() const;
 
 public Q_SLOTS:
     Q_SCRIPTABLE Q_NOREPLY void tryRaise(const QString &startupId);
@@ -114,13 +114,18 @@ protected:
     void showEvent(QShowEvent *event) override;
     void keyPressEvent(QKeyEvent *) override;
 
-private Q_SLOTS:
+public Q_SLOTS:
     void fileOpen();
+    void openUrl(const QUrl &url, const QString &serializedOptions = QString());
+    void openBookmark(const QUrl &url);
+
+    void hideWelcomeScreen();
+    void showWelcomeScreen(bool force = false);
 
+private Q_SLOTS:
     void slotUpdateFullScreen();
     void slotShowMenubar();
 
-    void openUrl(const QUrl &url, const QString &serializedOptions = QString());
     void showOpenRecentMenu();
     void closeUrl();
     void print();
@@ -151,12 +156,6 @@ private Q_SLOTS:
 
     void slotFitWindowToPage(const QSize pageViewSize, const QSize pageSize);
 
-    void hideWelcomeScreen();
-    void showWelcomeScreen();
-    void refreshRecentsOnWelcomeScreen();
-
-    void forgetRecentItem(QUrl const &url);
-
 Q_SIGNALS:
     void moveSplitter(int sideWidgetSize);
 
@@ -186,7 +185,7 @@ private:
     bool m_unique;
     QTabWidget *m_tabWidget;
     KToggleAction *m_openInTab;
-    WelcomeScreen *m_welcomeScreen;
+    WelcomeView *m_welcomeScreen;
     QStackedWidget *m_centralStackedWidget;
     Sidebar *m_sidebar = nullptr;
 
diff --git a/shell/shell.qrc b/shell/shell.qrc
index 73705f2287fce8424c8f61ddcf91a9355dca11d5..a5637261819d6c69789a006a78e2559279eccf60 100644
--- a/shell/shell.qrc
+++ b/shell/shell.qrc
@@ -2,5 +2,6 @@
 <RCC version="1.0">
     <qresource prefix="/kxmlgui5/okular">
         <file>shell.rc</file>
+        <file alias="okular.svgz">../icons/hisc-apps-okular.svgz</file>
     </qresource>
 </RCC>
diff --git a/shell/shell.rc b/shell/shell.rc
index d54f12715417e23fea2d240cdab733e35aefe295..81b230a41770062bc7476615bffc600948464452 100644
--- a/shell/shell.rc
+++ b/shell/shell.rc
@@ -18,6 +18,7 @@
   <Merge/>
   <Menu name="help">
    <DefineGroup append="about_merge" name="about_merge" />
+   <Action name="help_welcome_page" group="about_merge"/>
   </Menu>
  </MenuBar>
  <ToolBar noMerge="1" name="mainToolBar" >
diff --git a/shell/welcomescreen.cpp b/shell/welcomescreen.cpp
deleted file mode 100644
index e7b5f996307e007831a2e1f012254cd13396f35f..0000000000000000000000000000000000000000
--- a/shell/welcomescreen.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
-    SPDX-FileCopyrightText: 2021 Jiří Wolker <woljiri@gmail.com>
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include "welcomescreen.h"
-
-#include <KConfigGroup>
-#include <KIO/OpenFileManagerWindowJob>
-#include <KIconLoader>
-#include <KSharedConfig>
-
-#include <QAction>
-#include <QClipboard>
-#include <QGraphicsOpacityEffect>
-#include <QGuiApplication>
-#include <QMenu>
-#include <QResizeEvent>
-#include <QStyledItemDelegate>
-
-#include "recentitemsmodel.h"
-
-class RecentsListItemDelegate : public QStyledItemDelegate
-{
-    Q_OBJECT
-
-public:
-    explicit RecentsListItemDelegate(WelcomeScreen *welcomeScreen)
-        : m_welcomeScreen(welcomeScreen)
-    {
-    }
-
-    WelcomeScreen *welcomeScreen() const
-    {
-        return m_welcomeScreen;
-    }
-
-    bool editorEvent(QEvent *event, QAbstractItemModel *aModel, const QStyleOptionViewItem &styleOptionViewItem, const QModelIndex &index) override
-    {
-        const RecentItemsModel *model = static_cast<RecentItemsModel *>(aModel);
-        const RecentItemsModel::RecentItem *item = model->getItem(index);
-
-        QPoint menuPosition;
-
-        if (item != nullptr) {
-            bool willOpenMenu = false;
-            if (event->type() == QEvent::ContextMenu) {
-                willOpenMenu = true;
-                menuPosition = static_cast<QContextMenuEvent *>(event)->globalPos();
-            }
-            if (event->type() == QEvent::MouseButtonPress) {
-                if (static_cast<QMouseEvent *>(event)->button() == Qt::MouseButton::RightButton) {
-                    willOpenMenu = true;
-                    menuPosition = static_cast<QMouseEvent *>(event)->globalPosition().toPoint();
-                }
-            }
-
-            if (willOpenMenu) {
-                event->accept();
-
-                QMenu menu;
-
-                QAction *copyPathAction = new QAction(i18n("&Copy Path"));
-                copyPathAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy")));
-                connect(copyPathAction, &QAction::triggered, this, [item]() {
-                    QString path;
-                    if (item->url.isLocalFile()) {
-                        path = item->url.toLocalFile();
-                    } else {
-                        path = item->url.toString();
-                    }
-                    QGuiApplication::clipboard()->setText(path);
-                });
-                menu.addAction(copyPathAction);
-
-                QAction *showDirectoryAction = new QAction(i18n("&Open Containing Folder"));
-                showDirectoryAction->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
-                connect(showDirectoryAction, &QAction::triggered, this, [item]() {
-                    if (item->url.isLocalFile()) {
-                        KIO::highlightInFileManager({item->url});
-                    }
-                });
-                menu.addAction(showDirectoryAction);
-                if (!item->url.isLocalFile()) {
-                    showDirectoryAction->setEnabled(false);
-                }
-
-                QAction *forgetItemAction = new QAction(i18nc("recent items context menu", "&Forget This Item"));
-                forgetItemAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history")));
-                connect(forgetItemAction, &QAction::triggered, this, [this, item]() { Q_EMIT welcomeScreen()->forgetRecentItem(item->url); });
-                menu.addAction(forgetItemAction);
-
-                menu.exec(menuPosition);
-
-                return true;
-            }
-        }
-
-        return QStyledItemDelegate::editorEvent(event, aModel, styleOptionViewItem, index);
-    }
-
-private:
-    WelcomeScreen *m_welcomeScreen;
-};
-
-WelcomeScreen::WelcomeScreen(QWidget *parent)
-    : QWidget(parent)
-    , m_recentsModel(new RecentItemsModel)
-    , m_recentsItemDelegate(new RecentsListItemDelegate(this))
-{
-    Q_ASSERT(parent);
-
-    setupUi(this);
-
-    connect(openButton, &QPushButton::clicked, this, &WelcomeScreen::openClicked);
-    connect(closeButton, &QPushButton::clicked, this, &WelcomeScreen::closeClicked);
-
-    recentsListView->setContextMenuPolicy(Qt::DefaultContextMenu);
-    recentsListView->setModel(m_recentsModel);
-    recentsListView->setItemDelegate(m_recentsItemDelegate);
-    connect(recentsListView, &QListView::activated, this, &WelcomeScreen::recentsItemActivated);
-
-    connect(m_recentsModel, &RecentItemsModel::layoutChanged, this, &WelcomeScreen::recentListChanged);
-
-    QVBoxLayout *noRecentsLayout = new QVBoxLayout(recentsListView);
-    recentsListView->setLayout(noRecentsLayout);
-    m_noRecentsLabel = new QLabel(recentsListView);
-    QFont placeholderLabelFont;
-    // To match the size of a level 2 Heading/KTitleWidget
-    placeholderLabelFont.setPointSize(qRound(placeholderLabelFont.pointSize() * 1.3));
-    noRecentsLayout->addWidget(m_noRecentsLabel);
-    m_noRecentsLabel->setFont(placeholderLabelFont);
-    m_noRecentsLabel->setTextInteractionFlags(Qt::NoTextInteraction);
-    m_noRecentsLabel->setWordWrap(true);
-    m_noRecentsLabel->setAlignment(Qt::AlignCenter);
-    m_noRecentsLabel->setText(i18nc("on welcome screen", "No recent documents"));
-    // Match opacity of QML placeholder label component
-    auto *effect = new QGraphicsOpacityEffect(m_noRecentsLabel);
-    effect->setOpacity(0.5);
-    m_noRecentsLabel->setGraphicsEffect(effect);
-
-    connect(forgetAllButton, &QToolButton::clicked, this, &WelcomeScreen::forgetAllRecents);
-}
-
-WelcomeScreen::~WelcomeScreen()
-{
-    delete m_recentsModel;
-    delete m_recentsItemDelegate;
-}
-
-void WelcomeScreen::showEvent(QShowEvent *e)
-{
-    if (appIcon->pixmap(Qt::ReturnByValue).isNull()) {
-        appIcon->setPixmap(QIcon::fromTheme(QStringLiteral("okular")).pixmap(KIconLoader::SizeEnormous));
-    }
-
-    QWidget::showEvent(e);
-}
-
-void WelcomeScreen::loadRecents()
-{
-    m_recentsModel->loadEntries(KSharedConfig::openConfig()->group(QStringLiteral("Recent Files")));
-}
-
-int WelcomeScreen::recentsCount()
-{
-    return m_recentsModel->rowCount();
-}
-
-void WelcomeScreen::recentsItemActivated(const QModelIndex &index)
-{
-    const RecentItemsModel::RecentItem *item = m_recentsModel->getItem(index);
-    if (item != nullptr) {
-        Q_EMIT recentItemClicked(item->url);
-    }
-}
-
-void WelcomeScreen::recentListChanged()
-{
-    if (recentsCount() == 0) {
-        m_noRecentsLabel->show();
-        forgetAllButton->setEnabled(false);
-    } else {
-        m_noRecentsLabel->hide();
-        forgetAllButton->setEnabled(true);
-    }
-}
-
-#include "welcomescreen.moc"
diff --git a/shell/welcomescreen.h b/shell/welcomescreen.h
deleted file mode 100644
index 6c404951c5d53f8d1654f8d40ae7c200c97fc249..0000000000000000000000000000000000000000
--- a/shell/welcomescreen.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-    SPDX-FileCopyrightText: 2021 Jiří Wolker <woljiri@gmail.com>
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#ifndef WELCOMESCREEN_H
-#define WELCOMESCREEN_H
-
-#include "shell/ui_welcomescreen.h"
-
-#include <QFrame>
-#include <QUrl>
-
-class KRecentFilesAction;
-class QListWidgetItem;
-class RecentItemsModel;
-class RecentsListItemDelegate;
-
-class WelcomeScreen : public QWidget, Ui::WelcomeScreen
-{
-    Q_OBJECT
-public:
-    explicit WelcomeScreen(QWidget *parent = nullptr);
-    ~WelcomeScreen() override;
-
-    void loadRecents();
-
-Q_SIGNALS:
-    void openClicked();
-    void closeClicked();
-    void recentItemClicked(QUrl const &url);
-    void forgetAllRecents();
-    void forgetRecentItem(QUrl const &url);
-
-protected:
-    void showEvent(QShowEvent *e) override;
-
-private Q_SLOTS:
-    void recentsItemActivated(QModelIndex const &index);
-    void recentListChanged();
-
-private:
-    int recentsCount();
-
-    RecentItemsModel *m_recentsModel;
-    RecentsListItemDelegate *m_recentsItemDelegate;
-
-    QLabel *m_noRecentsLabel;
-};
-
-#endif // WELCOMESCREEN_H
diff --git a/shell/welcomescreen.ui b/shell/welcomescreen.ui
deleted file mode 100644
index 5c087836f0204cc212526f5cf79c81a38da01fec..0000000000000000000000000000000000000000
--- a/shell/welcomescreen.ui
+++ /dev/null
@@ -1,524 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-SPDX-FileCopyrightText: 2021 Jiří Wolker <woljiri@gmail.com>
-SPDX-License-Identifier: GPL-2.0-or-later
--->
-<ui version="4.0">
- <class>WelcomeScreen</class>
- <widget class="QWidget" name="WelcomeScreen">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>1423</width>
-    <height>850</height>
-   </rect>
-  </property>
-  <property name="acceptDrops">
-   <bool>true</bool>
-  </property>
-  <layout class="QGridLayout" name="gridLayout" rowstretch="3,5,0,3" columnstretch="1,4,1">
-   <property name="leftMargin">
-    <number>0</number>
-   </property>
-   <property name="topMargin">
-    <number>0</number>
-   </property>
-   <property name="rightMargin">
-    <number>0</number>
-   </property>
-   <property name="bottomMargin">
-    <number>0</number>
-   </property>
-   <property name="spacing">
-    <number>0</number>
-   </property>
-   <item row="1" column="0" rowspan="3">
-    <spacer name="horizontalSpacer_8">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="sizeType">
-      <enum>QSizePolicy::MinimumExpanding</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>48</width>
-       <height>20</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="0" column="1">
-    <spacer name="verticalSpacer_5">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeType">
-      <enum>QSizePolicy::MinimumExpanding</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>48</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="1" column="1" rowspan="2">
-    <widget class="QWidget" name="horizontalWidget" native="true">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
-       <horstretch>0</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="minimumSize">
-      <size>
-       <width>0</width>
-       <height>256</height>
-      </size>
-     </property>
-     <property name="maximumSize">
-      <size>
-       <width>700</width>
-       <height>16777215</height>
-      </size>
-     </property>
-     <layout class="QHBoxLayout" name="horizontalLayout_2">
-      <property name="spacing">
-       <number>0</number>
-      </property>
-      <property name="leftMargin">
-       <number>0</number>
-      </property>
-      <property name="topMargin">
-       <number>0</number>
-      </property>
-      <property name="rightMargin">
-       <number>0</number>
-      </property>
-      <property name="bottomMargin">
-       <number>0</number>
-      </property>
-      <item>
-       <widget class="QFrame" name="verticalFrame">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Maximum" vsizetype="Expanding">
-          <horstretch>1</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <property name="maximumSize">
-         <size>
-          <width>192</width>
-          <height>16777215</height>
-         </size>
-        </property>
-        <property name="frameShape">
-         <enum>QFrame::NoFrame</enum>
-        </property>
-        <layout class="QVBoxLayout" name="verticalLayout_5">
-         <property name="spacing">
-          <number>0</number>
-         </property>
-         <property name="leftMargin">
-          <number>0</number>
-         </property>
-         <property name="topMargin">
-          <number>0</number>
-         </property>
-         <property name="rightMargin">
-          <number>0</number>
-         </property>
-         <property name="bottomMargin">
-          <number>0</number>
-         </property>
-         <item>
-          <widget class="QLabel" name="label">
-           <property name="minimumSize">
-            <size>
-             <width>0</width>
-             <height>40</height>
-            </size>
-           </property>
-           <property name="font">
-            <font>
-             <pointsize>12</pointsize>
-             <weight>75</weight>
-             <bold>true</bold>
-            </font>
-           </property>
-           <property name="text">
-            <string>Welcome to Okular</string>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-           <property name="wordWrap">
-            <bool>true</bool>
-           </property>
-           <property name="margin">
-            <number>0</number>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <spacer name="verticalSpacer_2">
-           <property name="orientation">
-            <enum>Qt::Vertical</enum>
-           </property>
-           <property name="sizeType">
-            <enum>QSizePolicy::MinimumExpanding</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>12</width>
-             <height>12</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <widget class="QLabel" name="appIcon">
-           <property name="text">
-            <string notr="true"/>
-           </property>
-           <property name="alignment">
-            <set>Qt::AlignCenter</set>
-           </property>
-          </widget>
-         </item>
-         <item>
-          <spacer name="verticalSpacer_3">
-           <property name="orientation">
-            <enum>Qt::Vertical</enum>
-           </property>
-           <property name="sizeType">
-            <enum>QSizePolicy::MinimumExpanding</enum>
-           </property>
-           <property name="sizeHint" stdset="0">
-            <size>
-             <width>20</width>
-             <height>12</height>
-            </size>
-           </property>
-          </spacer>
-         </item>
-         <item>
-          <layout class="QHBoxLayout" name="horizontalLayout">
-           <property name="sizeConstraint">
-            <enum>QLayout::SetMinimumSize</enum>
-           </property>
-           <item>
-            <spacer name="horizontalSpacer">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeType">
-              <enum>QSizePolicy::Expanding</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>48</width>
-               <height>0</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-           <item>
-            <widget class="QPushButton" name="openButton">
-             <property name="text">
-              <string comment="on welcome screen">Open Document...</string>
-             </property>
-             <property name="icon">
-              <iconset theme="document-open" />
-             </property>
-            </widget>
-           </item>
-           <item>
-            <spacer name="horizontalSpacer_2">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeType">
-              <enum>QSizePolicy::Expanding</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>48</width>
-               <height>0</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-          </layout>
-         </item>
-        </layout>
-       </widget>
-      </item>
-      <item>
-       <spacer name="horizontalSpacer_4">
-        <property name="orientation">
-         <enum>Qt::Horizontal</enum>
-        </property>
-        <property name="sizeType">
-         <enum>QSizePolicy::Maximum</enum>
-        </property>
-        <property name="sizeHint" stdset="0">
-         <size>
-          <width>48</width>
-          <height>20</height>
-         </size>
-        </property>
-       </spacer>
-      </item>
-      <item>
-       <widget class="QWidget" name="verticalWidget" native="true">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <layout class="QVBoxLayout" name="verticalLayout" stretch="1">
-         <property name="spacing">
-          <number>0</number>
-         </property>
-         <item>
-          <widget class="Line" name="line_2">
-           <property name="sizePolicy">
-            <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
-             <horstretch>0</horstretch>
-             <verstretch>0</verstretch>
-            </sizepolicy>
-           </property>
-           <property name="orientation">
-            <enum>Qt::Vertical</enum>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </widget>
-      </item>
-      <item>
-       <spacer name="horizontalSpacer_3">
-        <property name="orientation">
-         <enum>Qt::Horizontal</enum>
-        </property>
-        <property name="sizeType">
-         <enum>QSizePolicy::Maximum</enum>
-        </property>
-        <property name="sizeHint" stdset="0">
-         <size>
-          <width>48</width>
-          <height>20</height>
-         </size>
-        </property>
-       </spacer>
-      </item>
-      <item>
-       <widget class="QWidget" name="recentsArea" native="true">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,1">
-         <property name="spacing">
-          <number>4</number>
-         </property>
-         <property name="leftMargin">
-          <number>0</number>
-         </property>
-         <property name="topMargin">
-          <number>0</number>
-         </property>
-         <property name="rightMargin">
-          <number>0</number>
-         </property>
-         <property name="bottomMargin">
-          <number>0</number>
-         </property>
-         <item>
-          <widget class="QWidget" name="horizontalWidget" native="true">
-           <property name="sizePolicy">
-            <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-             <horstretch>0</horstretch>
-             <verstretch>0</verstretch>
-            </sizepolicy>
-           </property>
-           <property name="minimumSize">
-            <size>
-             <width>0</width>
-             <height>36</height>
-            </size>
-           </property>
-           <layout class="QHBoxLayout" name="horizontalLayout_3">
-            <property name="spacing">
-             <number>0</number>
-            </property>
-            <property name="leftMargin">
-             <number>0</number>
-            </property>
-            <property name="rightMargin">
-             <number>0</number>
-            </property>
-            <item>
-             <widget class="QLabel" name="label_2">
-              <property name="sizePolicy">
-               <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
-                <horstretch>0</horstretch>
-                <verstretch>0</verstretch>
-               </sizepolicy>
-              </property>
-              <property name="font">
-               <font>
-                <pointsize>12</pointsize>
-               </font>
-              </property>
-              <property name="text">
-               <string comment="on welcome screen">Recent documents</string>
-              </property>
-              <property name="alignment">
-               <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-              </property>
-             </widget>
-            </item>
-            <item>
-             <widget class="QToolButton" name="forgetAllButton">
-              <property name="text">
-               <string comment="on welcome screen (recent document list)">Forget All</string>
-              </property>
-              <property name="icon">
-               <iconset theme="edit-clear-history"/>
-              </property>
-              <property name="toolButtonStyle">
-               <enum>Qt::ToolButtonTextBesideIcon</enum>
-              </property>
-              <property name="autoRaise">
-               <bool>true</bool>
-              </property>
-             </widget>
-            </item>
-           </layout>
-          </widget>
-         </item>
-         <item>
-          <widget class="QListView" name="recentsListView">
-           <property name="sizePolicy">
-            <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
-             <horstretch>0</horstretch>
-             <verstretch>1</verstretch>
-            </sizepolicy>
-           </property>
-           <property name="minimumSize">
-            <size>
-             <width>200</width>
-             <height>128</height>
-            </size>
-           </property>
-           <property name="verticalScrollBarPolicy">
-            <enum>Qt::ScrollBarAsNeeded</enum>
-           </property>
-           <property name="horizontalScrollBarPolicy">
-            <enum>Qt::ScrollBarAsNeeded</enum>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </widget>
-      </item>
-     </layout>
-    </widget>
-   </item>
-   <item row="0" column="2">
-    <layout class="QGridLayout" name="gridLayout_2">
-     <item row="0" column="0">
-      <spacer name="horizontalSpacer_5">
-       <property name="orientation">
-        <enum>Qt::Horizontal</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>40</width>
-         <height>20</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
-     <item row="0" column="1">
-      <widget class="QPushButton" name="closeButton">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="toolTip">
-        <string>Hide welcome screen</string>
-       </property>
-       <property name="text">
-        <string comment="on welcome screen"/>
-       </property>
-       <property name="icon">
-        <iconset theme="window-close"/>
-       </property>
-       <property name="flat">
-        <bool>true</bool>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <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>
-   </item>
-   <item row="1" column="2" rowspan="3">
-    <spacer name="horizontalSpacer_6">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>40</width>
-       <height>20</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="3" column="1">
-    <spacer name="verticalSpacer_6">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeType">
-      <enum>QSizePolicy::MinimumExpanding</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>20</width>
-       <height>48</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-  </layout>
- </widget>
- <tabstops>
-  <tabstop>openButton</tabstop>
- </tabstops>
- <resources/>
- <connections/>
-</ui>
diff --git a/shell/welcomeview/bookmarksmodel.cpp b/shell/welcomeview/bookmarksmodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c5c513d0583735ec6bb48eca52cfe4cc40648b5
--- /dev/null
+++ b/shell/welcomeview/bookmarksmodel.cpp
@@ -0,0 +1,52 @@
+/*
+ *  SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "bookmarksmodel.h"
+
+#include <QFileInfo>
+#include <QIcon>
+#include <QMimeDatabase>
+
+BookmarksModel::BookmarksModel(QObject *parent)
+    : QStandardItemModel(parent)
+{
+}
+
+void BookmarksModel::refresh(const QMultiMap<QUrl, Okular::BookmarksInterface::Bookmark> &bookmarks)
+{
+    clear();
+
+    const QList<QUrl> files = bookmarks.uniqueKeys();
+    for (const QUrl &file : files) {
+        QStandardItem *item = new QStandardItem;
+        item->setData(file);
+        item->setToolTip(file.toString(QUrl::PreferLocalFile));
+
+        if (file.isLocalFile()) {
+            const QFileInfo fileInfo(file.toLocalFile());
+            item->setIcon(QIcon::fromTheme(QMimeDatabase().mimeTypeForFile(fileInfo).iconName()));
+            item->setText(fileInfo.fileName());
+        } else {
+            item->setIcon(QIcon::fromTheme(QStringLiteral("network-server")));
+            item->setText(file.toString());
+        }
+
+        const QList<Okular::BookmarksInterface::Bookmark> bookmarkList = bookmarks.values(file);
+        for (const Okular::BookmarksInterface::Bookmark &bookmark : bookmarkList) {
+            QStandardItem *childItem = new QStandardItem;
+            childItem->setData(bookmark.url);
+            childItem->setText(bookmark.title);
+
+            item->appendRow(childItem);
+        }
+
+        appendRow(item);
+    }
+
+    sort(0);
+
+    Q_EMIT itemChanged(invisibleRootItem());
+}
diff --git a/shell/welcomeview/bookmarksmodel.h b/shell/welcomeview/bookmarksmodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..dba112de217ca24a2fbd9ee8585019e0ca6eb4fa
--- /dev/null
+++ b/shell/welcomeview/bookmarksmodel.h
@@ -0,0 +1,24 @@
+/*
+ *  SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef BOOKMARKSMODEL_H
+#define BOOKMARKSMODEL_H
+
+#include <QStandardItemModel>
+
+#include <interfaces/bookmarksinterface.h>
+
+class BookmarksModel : public QStandardItemModel
+{
+    Q_OBJECT
+
+public:
+    explicit BookmarksModel(QObject *parent = nullptr);
+
+    void refresh(const QMultiMap<QUrl, Okular::BookmarksInterface::Bookmark> &bookmarks);
+};
+
+#endif // BOOKMARKSMODEL_H
diff --git a/shell/welcomeview/recentfilesmodel.cpp b/shell/welcomeview/recentfilesmodel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c68208d3ce13dcc7396614688b48c86398d0c2c8
--- /dev/null
+++ b/shell/welcomeview/recentfilesmodel.cpp
@@ -0,0 +1,82 @@
+/*
+    SPDX-FileCopyrightText: 2022 Jiří Wolker <woljiri@gmail.com>
+    SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "recentfilesmodel.h"
+
+#include <QFileInfo>
+#include <QMimeDatabase>
+
+RecentFilesModel::RecentFilesModel(QObject *parent)
+    : QAbstractListModel(parent)
+{
+}
+
+QVariant RecentFilesModel::data(const QModelIndex &index, int role) const
+{
+    if (index.isValid()) {
+        const int row = index.row();
+        if (row >= 0 && row < m_recentFiles.count()) {
+            const RecentFileInfo &recentFile = m_recentFiles.at(row);
+            switch (role) {
+            case Qt::DisplayRole:
+                return recentFile.name;
+            case Qt::DecorationRole:
+                return recentFile.icon;
+            case Qt::ToolTipRole:
+                return recentFile.url.toString(QUrl::PreferLocalFile);
+            default:
+                break;
+            }
+        }
+    }
+
+    return QVariant();
+}
+
+int RecentFilesModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+
+    return m_recentFiles.count();
+}
+
+void RecentFilesModel::refresh(const QList<QUrl> &urls)
+{
+    QVector<RecentFileInfo> recentFiles;
+    recentFiles.reserve(urls.count());
+
+    QIcon icon;
+    QString name;
+    for (const QUrl &url : urls) {
+        if (url.isLocalFile()) {
+            const QFileInfo fileInfo(url.toLocalFile());
+            icon = QIcon::fromTheme(QMimeDatabase().mimeTypeForFile(fileInfo).iconName());
+            name = fileInfo.fileName();
+        } else {
+            icon = QIcon::fromTheme(QStringLiteral("network-server"));
+            name = url.toString();
+        }
+
+        recentFiles.append({icon, name, url});
+    }
+
+    beginResetModel();
+    m_recentFiles = std::move(recentFiles);
+    endResetModel();
+}
+
+QUrl RecentFilesModel::url(const QModelIndex &index) const
+{
+    if (index.isValid()) {
+        const int row = index.row();
+        if (row >= 0 && row < m_recentFiles.count()) {
+            return m_recentFiles.at(row).url;
+        }
+    }
+
+    return QUrl();
+}
diff --git a/shell/welcomeview/recentfilesmodel.h b/shell/welcomeview/recentfilesmodel.h
new file mode 100644
index 0000000000000000000000000000000000000000..de55f3fc6a894272f9f0aa82a6f1036f1356a6ac
--- /dev/null
+++ b/shell/welcomeview/recentfilesmodel.h
@@ -0,0 +1,38 @@
+/*
+    SPDX-FileCopyrightText: 2022 Jiří Wolker <woljiri@gmail.com>
+    SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifndef RECENTFILESMODEL_H
+#define RECENTFILESMODEL_H
+
+#include <QAbstractListModel>
+#include <QIcon>
+#include <QUrl>
+
+class RecentFilesModel : public QAbstractListModel
+{
+    Q_OBJECT
+
+public:
+    explicit RecentFilesModel(QObject *parent = nullptr);
+
+    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+
+    void refresh(const QList<QUrl> &urls);
+    QUrl url(const QModelIndex &index) const;
+
+private:
+    struct RecentFileInfo {
+        QIcon icon;
+        QString name;
+        QUrl url;
+    };
+
+    QVector<RecentFileInfo> m_recentFiles;
+};
+
+#endif // RECENTFILESMODEL_H
diff --git a/shell/welcomeview/welcomeview.cpp b/shell/welcomeview/welcomeview.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..94c29f9dc51b165efd5d8b14e731084b519e7697
--- /dev/null
+++ b/shell/welcomeview/welcomeview.cpp
@@ -0,0 +1,285 @@
+/*
+    SPDX-FileCopyrightText: 2022 Jiří Wolker <woljiri@gmail.com>
+    SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "welcomeview.h"
+
+#include "../shell.h"
+#include "bookmarksmodel.h"
+#include "recentfilesmodel.h"
+
+#include <KAboutData>
+#include <KConfigGroup>
+#include <KIO/OpenFileManagerWindowJob>
+#include <KIconLoader>
+#include <KRecentFilesAction>
+#include <KSharedConfig>
+
+#include <QClipboard>
+#include <QDesktopServices>
+#include <QGraphicsOpacityEffect>
+#include <QLabel>
+#include <QMenu>
+#include <QTimer>
+
+#include <interfaces/bookmarksinterface.h>
+
+class Placeholder : public QLabel // clazy:exclude=missing-qobject-macro
+{
+public:
+    explicit Placeholder(QWidget *parent = nullptr)
+        : QLabel(parent)
+    {
+        setAlignment(Qt::AlignCenter);
+        setMargin(20);
+        setWordWrap(true);
+        // Match opacity of QML placeholder label component
+        QGraphicsOpacityEffect *opacityEffect = new QGraphicsOpacityEffect;
+        opacityEffect->setOpacity(0.5);
+        setGraphicsEffect(opacityEffect);
+    }
+};
+
+WelcomeView::WelcomeView(Shell *shell, KParts::ReadWritePart *part, QWidget *parent)
+    : QScrollArea(parent)
+    , m_shell(shell)
+    , m_bookmarks(qobject_cast<Okular::BookmarksInterface *>(part))
+{
+    setupUi(this);
+
+    m_placeholderRecentFiles = new Placeholder;
+    m_placeholderRecentFiles->setText(i18n("No recent files"));
+
+    QVBoxLayout *layoutPlaceholderRecentFiles = new QVBoxLayout;
+    layoutPlaceholderRecentFiles->addWidget(m_placeholderRecentFiles);
+    listViewRecentFiles->setLayout(layoutPlaceholderRecentFiles);
+
+    m_recentFilesModel = new RecentFilesModel(this);
+    connect(m_recentFilesModel, &RecentFilesModel::modelReset, this, [this]() {
+        const bool noRecentFiles = m_recentFilesModel->rowCount() == 0;
+        buttonClearRecentFiles->setDisabled(noRecentFiles);
+        m_placeholderRecentFiles->setVisible(noRecentFiles);
+    });
+
+    KRecentFilesAction *recentFilesAction = m_shell->recentFilesAction();
+    m_recentFilesModel->refresh(recentFilesAction->urls());
+    recentFilesAction->menu()->installEventFilter(this);
+
+    listViewRecentFiles->setModel(m_recentFilesModel);
+    connect(listViewRecentFiles, &QListView::customContextMenuRequested, this, &WelcomeView::onRecentFilesContextMenuRequested);
+    connect(listViewRecentFiles, &QListView::activated, this, [this](const QModelIndex &index) {
+        if (index.isValid()) {
+            const QUrl url = m_recentFilesModel->url(index);
+            Q_ASSERT(url.isValid());
+            m_shell->openUrl(url);
+        }
+    });
+
+    connect(buttonOpenFile, &QPushButton::clicked, m_shell, &Shell::fileOpen);
+    connect(buttonImportPostScript, SIGNAL(clicked()), part, SLOT(slotImportPSFile())); // clazy:exclude=old-style-connect
+    connect(buttonClearRecentFiles, &QPushButton::clicked, recentFilesAction, &KRecentFilesAction::clear);
+
+    m_placeholderBookmarks = new Placeholder;
+    m_placeholderBookmarks->setText(i18n("No bookmarks"));
+
+    QVBoxLayout *layoutPlaceholderBookmarks = new QVBoxLayout;
+    layoutPlaceholderBookmarks->addWidget(m_placeholderBookmarks);
+    treeViewBookmarks->setLayout(layoutPlaceholderBookmarks);
+
+    m_bookmarksModel = new BookmarksModel(this);
+    connect(m_bookmarksModel, &BookmarksModel::itemChanged, this, [this]() {
+        const bool noBookmarks = m_bookmarksModel->rowCount() == 0;
+        m_placeholderBookmarks->setVisible(noBookmarks);
+    });
+
+    connect(part, SIGNAL(bookmarksChanged()), this, SLOT(onBookmarksChanged())); // clazy:exclude=old-style-connect
+    onBookmarksChanged();
+
+    treeViewBookmarks->setModel(m_bookmarksModel);
+    connect(treeViewBookmarks, &QListView::customContextMenuRequested, this, &WelcomeView::onBookmarksContextMenuRequested);
+    connect(treeViewBookmarks, &QTreeView::activated, this, [this](const QModelIndex &index) {
+        if (index.isValid()) {
+            const QStandardItem *item = m_bookmarksModel->itemFromIndex(index);
+            if (item && item->parent()) {
+                m_shell->openBookmark(item->data().toUrl());
+            }
+        }
+    });
+
+    connect(labelHomepage, qOverload<>(&KUrlLabel::leftClickedUrl), this, []() { QDesktopServices::openUrl(QUrl(KAboutData::applicationData().homepage())); });
+    connect(labelHandbook, qOverload<>(&KUrlLabel::leftClickedUrl), m_shell, &Shell::appHelpActivated);
+    connect(buttonClose, &QPushButton::clicked, m_shell, &Shell::hideWelcomeScreen);
+
+    static const char showOnStartupKey[] = "ShowWelcomeScreenOnStartup";
+    KConfigGroup configGroup = KSharedConfig::openConfig()->group(QStringLiteral("General"));
+    checkBoxShowOnStartup->setChecked(configGroup.readEntry(showOnStartupKey, true));
+    connect(checkBoxShowOnStartup, &QCheckBox::toggled, this, [configGroup](bool checked) mutable { configGroup.writeEntry(showOnStartupKey, checked); });
+
+    updateFonts();
+    updateButtons();
+}
+
+bool WelcomeView::event(QEvent *event)
+{
+    switch (event->type()) {
+    case QEvent::FontChange:
+        updateFonts();
+        updateButtons();
+        break;
+    case QEvent::Resize:
+        if (updateLayout() && isVisible()) {
+            return true;
+        }
+        break;
+    default:
+        break;
+    }
+
+    return QScrollArea::event(event);
+}
+
+bool WelcomeView::eventFilter(QObject *watched, QEvent *event)
+{
+    KRecentFilesAction *recentFilesAction = m_shell->recentFilesAction();
+    if (watched == recentFilesAction->menu()) {
+        switch (event->type()) {
+        case QEvent::ActionAdded:
+        case QEvent::ActionRemoved:
+            // since the KRecentFilesAction doesn't notify about adding or
+            // deleting items, we should use this workaround to find out
+            // the KRecentFilesAction has changed
+            QTimer::singleShot(0, this, [this, recentFilesAction]() { m_recentFilesModel->refresh(recentFilesAction->urls()); });
+            break;
+        default:
+            break;
+        }
+    }
+
+    return QScrollArea::eventFilter(watched, event);
+}
+
+void WelcomeView::onBookmarksChanged()
+{
+    m_bookmarksModel->refresh(m_bookmarks->getBookmarks());
+}
+
+void WelcomeView::onRecentFilesContextMenuRequested(QPoint pos)
+{
+    const QModelIndex index = listViewRecentFiles->indexAt(pos);
+    if (!index.isValid()) {
+        return;
+    }
+
+    const QUrl url = m_recentFilesModel->url(index);
+    Q_ASSERT(url.isValid());
+
+    QMenu contextMenu;
+
+    QAction *action = contextMenu.addAction(i18n("Copy &Location"));
+    action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy-path")));
+    connect(action, &QAction::triggered, this, [url]() { qApp->clipboard()->setText(url.toString(QUrl::PreferLocalFile)); });
+
+    action = contextMenu.addAction(i18n("&Open Containing Folder"));
+    action->setEnabled(url.isLocalFile());
+    action->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
+    connect(action, &QAction::triggered, this, [url]() { KIO::highlightInFileManager({url}); });
+
+    action = contextMenu.addAction(i18n("&Remove"));
+    action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
+    connect(action, &QAction::triggered, this, [this, url]() {
+        KRecentFilesAction *recentFilesAction = m_shell->recentFilesAction();
+        recentFilesAction->removeUrl(url);
+    });
+
+    contextMenu.exec(listViewRecentFiles->mapToGlobal(pos));
+}
+
+void WelcomeView::onBookmarksContextMenuRequested(QPoint pos)
+{
+    if (m_bookmarksModel->rowCount() == 0) {
+        return;
+    }
+
+    QMenu contextMenu;
+
+    const QModelIndex index = treeViewBookmarks->indexAt(pos);
+    if (index.isValid()) {
+        const QStandardItem *item = m_bookmarksModel->itemFromIndex(index);
+        if (item && !item->parent()) {
+            const QUrl &url = item->data().toUrl();
+            Q_ASSERT(url.isValid());
+
+            QAction *action = contextMenu.addAction(i18n("Copy &Location"));
+            action->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy-path")));
+            connect(action, &QAction::triggered, this, [url]() { qApp->clipboard()->setText(url.toString(QUrl::PreferLocalFile)); });
+
+            action = contextMenu.addAction(i18n("&Open Containing Folder"));
+            action->setEnabled(url.isLocalFile());
+            action->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder")));
+            connect(action, &QAction::triggered, this, [url]() { KIO::highlightInFileManager({url}); });
+
+            contextMenu.addSeparator();
+        }
+    }
+
+    QAction *action = contextMenu.addAction(i18n("Expand All"));
+    action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down-double")));
+    connect(action, &QAction::triggered, treeViewBookmarks, &QTreeView::expandAll);
+
+    action = contextMenu.addAction(i18n("Collapse All"));
+    action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up-double")));
+    connect(action, &QAction::triggered, treeViewBookmarks, &QTreeView::collapseAll);
+
+    contextMenu.exec(treeViewBookmarks->mapToGlobal(pos));
+}
+
+void WelcomeView::updateButtons()
+{
+    QVector<QPushButton *> buttons {buttonOpenFile, buttonImportPostScript};
+    const int maxWidth = std::accumulate(buttons.cbegin(), buttons.cend(), 0, [](int maxWidth, const QPushButton *button) { return std::max(maxWidth, button->sizeHint().width()); });
+    for (QPushButton *button : qAsConst(buttons)) {
+        button->setFixedWidth(maxWidth);
+    }
+
+    layoutBookmarks->setContentsMargins(maxWidth + layoutRecentFiles->horizontalSpacing(), 0, 0, 0);
+}
+
+void WelcomeView::updateFonts()
+{
+    QFont panelTitleFont = font();
+    panelTitleFont.setPointSize(panelTitleFont.pointSize() + 2);
+    labelRecentFiles->setFont(panelTitleFont);
+    labelBookmarks->setFont(panelTitleFont);
+    labelHelp->setFont(panelTitleFont);
+
+    QFont placeholderFont = font();
+    placeholderFont.setPointSize(qRound(placeholderFont.pointSize() * 1.3));
+    m_placeholderRecentFiles->setFont(placeholderFont);
+    m_placeholderBookmarks->setFont(placeholderFont);
+}
+
+bool WelcomeView::updateLayout()
+{
+    // align labelHelp with labelRecentFiles
+    labelHelp->setMinimumHeight(labelRecentFiles->height());
+
+    // show/hide widgetHelp depending on the view width
+    int implicitWidth = layoutRoot->minimumSize().width();
+    if (widgetHelp->isVisible()) {
+        if (width() <= implicitWidth) {
+            widgetHelp->hide();
+            return true;
+        }
+    } else {
+        implicitWidth += widgetHelp->width() + layoutPanels->spacing();
+        if (width() > implicitWidth) {
+            widgetHelp->show();
+            return true;
+        }
+    }
+
+    return false;
+}
diff --git a/shell/welcomeview/welcomeview.h b/shell/welcomeview/welcomeview.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b0c9fba8d3bb7146f9e735ed74e9a42f508bebb
--- /dev/null
+++ b/shell/welcomeview/welcomeview.h
@@ -0,0 +1,57 @@
+/*
+    SPDX-FileCopyrightText: 2022 Jiří Wolker <woljiri@gmail.com>
+    SPDX-FileCopyrightText: 2022 Eugene Popov <popov895@ukr.net>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifndef WELCOMEVIEW_H
+#define WELCOMEVIEW_H
+
+#include "shell/ui_welcomeview.h"
+
+class BookmarksModel;
+class Placeholder;
+class RecentFilesModel;
+class Shell;
+
+namespace KParts
+{
+class ReadWritePart;
+}
+
+namespace Okular
+{
+class BookmarksInterface;
+}
+
+class WelcomeView : public QScrollArea, Ui::WelcomeView
+{
+    Q_OBJECT
+
+public:
+    explicit WelcomeView(Shell *shell, KParts::ReadWritePart *part, QWidget *parent = nullptr);
+
+protected:
+    bool event(QEvent *event) override;
+    bool eventFilter(QObject *watched, QEvent *event) override;
+
+private Q_SLOTS:
+    void onBookmarksChanged();
+    void onRecentFilesContextMenuRequested(QPoint pos);
+    void onBookmarksContextMenuRequested(QPoint pos);
+
+private:
+    void updateButtons();
+    void updateFonts();
+    bool updateLayout();
+
+    Shell *m_shell = nullptr;
+    Okular::BookmarksInterface *m_bookmarks = nullptr;
+    RecentFilesModel *m_recentFilesModel = nullptr;
+    BookmarksModel *m_bookmarksModel = nullptr;
+    Placeholder *m_placeholderRecentFiles = nullptr;
+    Placeholder *m_placeholderBookmarks = nullptr;
+};
+
+#endif // WELCOMEVIEW_H
diff --git a/shell/welcomeview/welcomeview.ui b/shell/welcomeview/welcomeview.ui
new file mode 100644
index 0000000000000000000000000000000000000000..4c7d1e7af9b6465a9277407f07fef819b2c1ea61
--- /dev/null
+++ b/shell/welcomeview/welcomeview.ui
@@ -0,0 +1,415 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>WelcomeView</class>
+ <widget class="QScrollArea" name="WelcomeView">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1011</width>
+    <height>803</height>
+   </rect>
+  </property>
+  <property name="acceptDrops">
+   <bool>true</bool>
+  </property>
+  <property name="windowTitle">
+   <string>Welcome</string>
+  </property>
+  <property name="frameShape">
+   <enum>QFrame::NoFrame</enum>
+  </property>
+  <property name="widgetResizable">
+   <bool>true</bool>
+  </property>
+  <widget class="QWidget" name="scrollAreaWidgetContents">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>1011</width>
+     <height>803</height>
+    </rect>
+   </property>
+   <layout class="QGridLayout" name="layoutRoot" rowstretch="1,6,1" columnstretch="1,3,1">
+    <item row="0" column="1">
+     <spacer name="spacer6">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>0</width>
+        <height>0</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="1" column="0">
+     <spacer name="spacer4">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>0</width>
+        <height>0</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="1" column="1">
+     <widget class="QFrame" name="frameContent">
+      <layout class="QVBoxLayout" name="layoutContent" stretch="1,0">
+       <property name="leftMargin">
+         <number>0</number>
+       </property>
+       <property name="topMargin">
+         <number>0</number>
+       </property>
+       <property name="rightMargin">
+         <number>0</number>
+       </property>
+       <property name="bottomMargin">
+         <number>0</number>
+       </property>
+       <property name="spacing">
+        <number>20</number>
+       </property>
+       <item>
+        <layout class="QGridLayout" name="layoutPanels" columnstretch="1,0">
+         <property name="spacing">
+          <number>20</number>
+         </property>
+         <item row="0" column="0">
+          <widget class="QWidget" name="widgetRecentFiles" native="true">
+           <layout class="QGridLayout" name="layoutRecentFiles" rowstretch="0,0,0,1" columnstretch="0,1,0">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <property name="horizontalSpacing">
+             <number>20</number>
+            </property>
+            <item row="1" column="1" rowspan="3" colspan="2">
+             <widget class="QListView" name="listViewRecentFiles">
+              <property name="minimumSize">
+               <size>
+                <width>400</width>
+                <height>200</height>
+               </size>
+              </property>
+              <property name="contextMenuPolicy">
+               <enum>Qt::CustomContextMenu</enum>
+              </property>
+              <property name="editTriggers">
+               <set>QAbstractItemView::NoEditTriggers</set>
+              </property>
+              <property name="alternatingRowColors">
+               <bool>true</bool>
+              </property>
+              <property name="horizontalScrollBarPolicy">
+               <enum>Qt::ScrollBarAlwaysOff</enum>
+              </property>
+              <property name="textElideMode">
+               <enum>Qt::ElideMiddle</enum>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <widget class="QPushButton" name="buttonOpenFile">
+              <property name="text">
+               <string>Open File...</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="2">
+             <widget class="QPushButton" name="buttonClearRecentFiles">
+              <property name="toolTip">
+               <string>Clear Recent Files</string>
+              </property>
+              <property name="icon">
+               <iconset theme="edit-clear-all">
+                <normaloff>.</normaloff>.</iconset>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <widget class="QLabel" name="labelRecentFiles">
+              <property name="text">
+               <string>Recent Files</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <spacer name="spacer3">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>0</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="2" column="0">
+             <widget class="QPushButton" name="buttonImportPostScript">
+              <property name="text">
+               <string>Import PostScript...</string>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item row="0" column="1" rowspan="2">
+          <widget class="QWidget" name="widgetHelp" native="true">
+           <layout class="QVBoxLayout" name="layoutHelp" stretch="0,0,0,1">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="labelHelp">
+              <property name="text">
+               <string>Help</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+              </property>
+             </widget>
+            </item>
+            <item alignment="Qt::AlignLeft">
+             <widget class="KUrlLabel" name="labelHomepage">
+              <property name="text">
+               <string>Visit homepage</string>
+              </property>
+              <property name="url">
+               <string/>
+              </property>
+              <property name="glowEnabled">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item alignment="Qt::AlignLeft">
+             <widget class="KUrlLabel" name="labelHandbook">
+              <property name="text">
+               <string>Read handbook</string>
+              </property>
+              <property name="url">
+               <string/>
+              </property>
+              <property name="glowEnabled">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <spacer name="spacer11">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>0</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QWidget" name="widgetBookmarks" native="true">
+           <layout class="QVBoxLayout" name="layoutBookmarks">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <item>
+             <widget class="QLabel" name="labelBookmarks">
+              <property name="text">
+               <string>Bookmarks</string>
+              </property>
+              <property name="alignment">
+               <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QTreeView" name="treeViewBookmarks">
+              <property name="minimumSize">
+               <size>
+                <width>0</width>
+                <height>200</height>
+               </size>
+              </property>
+              <property name="contextMenuPolicy">
+               <enum>Qt::CustomContextMenu</enum>
+              </property>
+              <property name="editTriggers">
+               <set>QAbstractItemView::NoEditTriggers</set>
+              </property>
+              <property name="alternatingRowColors">
+               <bool>true</bool>
+              </property>
+              <property name="horizontalScrollBarPolicy">
+               <enum>Qt::ScrollBarAlwaysOff</enum>
+              </property>
+              <property name="textElideMode">
+               <enum>Qt::ElideMiddle</enum>
+              </property>
+              <attribute name="headerVisible">
+               <bool>false</bool>
+              </attribute>
+             </widget>
+            </item>
+           </layout>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <widget class="QWidget" name="widgetFooter" native="true">
+         <layout class="QHBoxLayout" name="layoutFooter">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <spacer name="spacer9">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>0</width>
+              <height>0</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="checkBoxShowOnStartup">
+            <property name="text">
+             <string>Show on startup</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="spacer10">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>0</width>
+              <height>0</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </item>
+    <item row="1" column="2">
+     <spacer name="spacer5">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>0</width>
+        <height>0</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="2" column="1">
+     <spacer name="spacer7">
+      <property name="orientation">
+       <enum>Qt::Vertical</enum>
+      </property>
+      <property name="sizeHint" stdset="0">
+       <size>
+        <width>0</width>
+        <height>0</height>
+       </size>
+      </property>
+     </spacer>
+    </item>
+    <item row="0" column="2" alignment="Qt::AlignRight|Qt::AlignTop">
+     <widget class="QPushButton" name="buttonClose">
+      <property name="toolTip">
+       <string>Close Welcome Page</string>
+      </property>
+      <property name="icon">
+       <iconset theme="window-close"/>
+      </property>
+      <property name="flat">
+       <bool>true</bool>
+      </property>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KUrlLabel</class>
+   <extends>QLabel</extends>
+   <header>kurllabel.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>buttonOpenFile</tabstop>
+  <tabstop>buttonImportPostScript</tabstop>
+  <tabstop>listViewRecentFiles</tabstop>
+  <tabstop>buttonClearRecentFiles</tabstop>
+  <tabstop>treeViewBookmarks</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
openSUSE Build Service is sponsored by