Преглед на файлове

Ladybird: Rudimentary tabbed browsing support

This patch removes the browser WebView from the window and places it
inside a Tab object, all wrapped up in a QT tab control. So far you can
create tabs, but can't close them.
Matthew Costa преди 3 години
родител
ревизия
8af5b49cba
променени са 5 файла, в които са добавени 179 реда и са изтрити 40 реда
  1. 45 30
      Ladybird/BrowserWindow.cpp
  2. 20 10
      Ladybird/BrowserWindow.h
  3. 1 0
      Ladybird/CMakeLists.txt
  4. 72 0
      Ladybird/Tab.cpp
  5. 41 0
      Ladybird/Tab.h

+ 45 - 30
Ladybird/BrowserWindow.cpp

@@ -1,55 +1,75 @@
+/*
+ * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
 #include "BrowserWindow.h"
 #include "BrowserWindow.h"
 #include "WebView.h"
 #include "WebView.h"
 #include <LibCore/EventLoop.h>
 #include <LibCore/EventLoop.h>
 #include <QAction>
 #include <QAction>
 #include <QStatusBar>
 #include <QStatusBar>
 
 
-extern String s_serenity_resource_root;
-
 BrowserWindow::BrowserWindow(Core::EventLoop& event_loop)
 BrowserWindow::BrowserWindow(Core::EventLoop& event_loop)
     : m_event_loop(event_loop)
     : m_event_loop(event_loop)
 {
 {
-    m_toolbar = new QToolBar;
+    m_tabs_container = new QTabWidget;
+    m_tabs_container->setElideMode(Qt::TextElideMode::ElideRight);
+    m_tabs_container->setMovable(true);
+    m_tabs_container->setTabsClosable(true);
 
 
-    auto reload_icon_path = QString("%1/res/icons/16x16/reload.png").arg(s_serenity_resource_root.characters());
-    auto* reload_action = new QAction(QIcon(reload_icon_path), "Reload");
-    reload_action->setShortcut(QKeySequence("Ctrl+R"));
-    m_toolbar->addAction(reload_action);
+    auto menu = menuBar()->addMenu("File");
+    auto new_tab_action = menu->addAction("New Tab", QKeySequence(Qt::CTRL | Qt::Key_T));
+    auto quit_action = menu->addAction("Quit", QKeySequence(Qt::CTRL | Qt::Key_Q));
 
 
-    m_location_edit = new QLineEdit;
-    m_toolbar->addWidget(m_location_edit);
+    QObject::connect(new_tab_action, &QAction::triggered, this, &BrowserWindow::new_tab);
+    QObject::connect(quit_action, &QAction::triggered, this, &QMainWindow::close);
+    QObject::connect(m_tabs_container, &QTabWidget::currentChanged, [this](int index) {
+        setWindowTitle(m_tabs_container->tabText(index));
+        setWindowIcon(m_tabs_container->tabIcon(index));
+    });
 
 
-    addToolBar(m_toolbar);
+    new_tab();
+
+    setCentralWidget(m_tabs_container);
+}
 
 
-    m_view = new WebView;
-    setCentralWidget(m_view);
+void BrowserWindow::new_tab()
+{
+    auto tab = make<Tab>(this);
+    auto tab_ptr = tab.ptr();
+    m_tabs.append(std::move(tab));
 
 
-    QObject::connect(m_view, &WebView::linkHovered, statusBar(), &QStatusBar::showMessage);
-    QObject::connect(m_view, &WebView::linkUnhovered, statusBar(), &QStatusBar::clearMessage);
+    if (m_current_tab == nullptr) {
+        m_current_tab = tab_ptr;
+    }
 
 
-    QObject::connect(m_view, &WebView::loadStarted, m_location_edit, &QLineEdit::setText);
-    QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &BrowserWindow::location_edit_return_pressed);
-    QObject::connect(m_view, &WebView::title_changed, this, &BrowserWindow::page_title_changed);
-    QObject::connect(m_view, &WebView::favicon_changed, this, &BrowserWindow::page_favicon_changed);
+    m_tabs_container->addTab(tab_ptr, "New Tab");
 
 
-    QObject::connect(reload_action, &QAction::triggered, this, &BrowserWindow::reload);
+    QObject::connect(tab_ptr, &Tab::title_changed, this, &BrowserWindow::tab_title_changed);
+    QObject::connect(tab_ptr, &Tab::favicon_changed, this, &BrowserWindow::tab_favicon_changed);
 }
 }
 
 
-void BrowserWindow::location_edit_return_pressed()
+int BrowserWindow::tab_index(Tab* tab)
 {
 {
-    view().load(m_location_edit->text().toUtf8().data());
+    return m_tabs_container->indexOf(tab);
 }
 }
 
 
-void BrowserWindow::page_title_changed(QString title)
+void BrowserWindow::tab_title_changed(int index, QString const& title)
 {
 {
-    if (title.isEmpty())
+    if (title.isEmpty()) {
+        m_tabs_container->setTabText(index, "...");
         setWindowTitle("Ladybird");
         setWindowTitle("Ladybird");
-    else
+    } else {
+        m_tabs_container->setTabText(index, title);
         setWindowTitle(QString("%1 - Ladybird").arg(title));
         setWindowTitle(QString("%1 - Ladybird").arg(title));
+    }
 }
 }
 
 
-void BrowserWindow::page_favicon_changed(QIcon icon)
+void BrowserWindow::tab_favicon_changed(int index, QIcon icon)
 {
 {
+    m_tabs_container->setTabIcon(index, icon);
     setWindowIcon(icon);
     setWindowIcon(icon);
 }
 }
 
 
@@ -62,8 +82,3 @@ void BrowserWindow::closeEvent(QCloseEvent* event)
     //        all of the browser windows have closed.
     //        all of the browser windows have closed.
     m_event_loop.quit(0);
     m_event_loop.quit(0);
 }
 }
-
-void BrowserWindow::reload()
-{
-    view().reload();
-}

+ 20 - 10
Ladybird/BrowserWindow.h

@@ -1,7 +1,17 @@
+/*
+ * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "Tab.h"
+#include <AK/Vector.h>
 #include <LibCore/Forward.h>
 #include <LibCore/Forward.h>
 #include <QIcon>
 #include <QIcon>
 #include <QLineEdit>
 #include <QLineEdit>
 #include <QMainWindow>
 #include <QMainWindow>
+#include <QMenuBar>
+#include <QTabWidget>
 #include <QToolBar>
 #include <QToolBar>
 
 
 #pragma once
 #pragma once
@@ -13,21 +23,21 @@ class BrowserWindow : public QMainWindow {
 public:
 public:
     explicit BrowserWindow(Core::EventLoop&);
     explicit BrowserWindow(Core::EventLoop&);
 
 
-    WebView& view() { return *m_view; }
+    WebView& view() const { return m_current_tab->view(); }
 
 
-    virtual void closeEvent(QCloseEvent*) override;
+    int tab_index(Tab*);
 
 
-public slots:
-    void location_edit_return_pressed();
-    void page_title_changed(QString);
-    void page_favicon_changed(QIcon);
+    virtual void closeEvent(QCloseEvent*) override;
 
 
 public slots:
 public slots:
-    void reload();
+    void tab_title_changed(int index, QString const&);
+    void tab_favicon_changed(int index, QIcon icon);
+    void new_tab();
 
 
 private:
 private:
-    QToolBar* m_toolbar { nullptr };
-    QLineEdit* m_location_edit { nullptr };
-    WebView* m_view { nullptr };
+    QTabWidget* m_tabs_container { nullptr };
+    Vector<NonnullOwnPtr<Tab>> m_tabs;
+    Tab* m_current_tab { nullptr };
+
     Core::EventLoop& m_event_loop;
     Core::EventLoop& m_event_loop;
 };
 };

+ 1 - 0
Ladybird/CMakeLists.txt

@@ -37,6 +37,7 @@ set(SOURCES
     RequestManagerQt.cpp
     RequestManagerQt.cpp
     main.cpp
     main.cpp
     WebView.cpp
     WebView.cpp
+    Tab.cpp
 )
 )
 
 
 add_executable(ladybird ${SOURCES})
 add_executable(ladybird ${SOURCES})

+ 72 - 0
Ladybird/Tab.cpp

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "Tab.h"
+#include "BrowserWindow.h"
+#include <QCoreApplication>
+#include <QStatusBar>
+#include <utility>
+
+extern String s_serenity_resource_root;
+
+Tab::Tab(QMainWindow* window)
+    : m_window(window)
+{
+    m_layout = new QBoxLayout(QBoxLayout::Direction::TopToBottom, this);
+    m_layout->setContentsMargins(0, 0, 0, 0);
+
+    m_view = new WebView;
+    m_toolbar = new QToolBar;
+    m_location_edit = new QLineEdit;
+
+    m_layout->addWidget(m_toolbar);
+    m_layout->addWidget(m_view);
+
+    auto reload_icon_path = QString("%1/res/icons/16x16/reload.png").arg(s_serenity_resource_root.characters());
+    auto* reload_action = new QAction(QIcon(reload_icon_path), "Reload");
+    reload_action->setShortcut(QKeySequence("Ctrl+R"));
+    m_toolbar->addAction(reload_action);
+    m_toolbar->addWidget(m_location_edit);
+
+    QObject::connect(m_view, &WebView::linkHovered, m_window->statusBar(), &QStatusBar::showMessage);
+    QObject::connect(m_view, &WebView::linkUnhovered, m_window->statusBar(), &QStatusBar::clearMessage);
+
+    QObject::connect(m_view, &WebView::loadStarted, m_location_edit, &QLineEdit::setText);
+    QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &Tab::location_edit_return_pressed);
+    QObject::connect(m_view, &WebView::title_changed, this, &Tab::page_title_changed);
+    QObject::connect(m_view, &WebView::favicon_changed, this, &Tab::page_favicon_changed);
+
+    QObject::connect(reload_action, &QAction::triggered, this, &Tab::reload);
+}
+
+void Tab::reload()
+{
+    view().reload();
+}
+
+void Tab::location_edit_return_pressed()
+{
+    view().load(m_location_edit->text().toUtf8().data());
+}
+
+void Tab::page_title_changed(QString title)
+{
+    emit title_changed(tab_index(), std::move(title));
+}
+
+void Tab::page_favicon_changed(QIcon icon)
+{
+    emit favicon_changed(tab_index(), std::move(icon));
+}
+
+int Tab::tab_index()
+{
+    // FIXME: I hear you like footguns...
+    //        There has to be a better way of doing this
+    auto browser_window = reinterpret_cast<BrowserWindow*>(m_window);
+    return browser_window->tab_index(this);
+}

+ 41 - 0
Ladybird/Tab.h

@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "WebView.h"
+#include <QBoxLayout>
+#include <QLineEdit>
+#include <QToolBar>
+#include <QWidget>
+
+class Tab final : public QWidget {
+    Q_OBJECT
+public:
+    explicit Tab(QMainWindow* window);
+
+    WebView& view() { return *m_view; }
+
+public slots:
+    void location_edit_return_pressed();
+    void page_title_changed(QString);
+    void page_favicon_changed(QIcon);
+    void reload();
+
+signals:
+    void title_changed(int id, QString);
+    void favicon_changed(int id, QIcon);
+
+private:
+    QBoxLayout* m_layout;
+    QToolBar* m_toolbar { nullptr };
+    QLineEdit* m_location_edit { nullptr };
+    WebView* m_view { nullptr };
+    QMainWindow* m_window { nullptr };
+
+    int tab_index();
+};