Skip to content

Commit 8af5b49

Browse files
ucostyADKaster
authored andcommitted
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.
1 parent ec44691 commit 8af5b49

File tree

5 files changed

+179
-40
lines changed

5 files changed

+179
-40
lines changed

Ladybird/BrowserWindow.cpp

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,75 @@
1+
/*
2+
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
3+
* Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
4+
*
5+
* SPDX-License-Identifier: BSD-2-Clause
6+
*/
7+
18
#include "BrowserWindow.h"
29
#include "WebView.h"
310
#include <LibCore/EventLoop.h>
411
#include <QAction>
512
#include <QStatusBar>
613

7-
extern String s_serenity_resource_root;
8-
914
BrowserWindow::BrowserWindow(Core::EventLoop& event_loop)
1015
: m_event_loop(event_loop)
1116
{
12-
m_toolbar = new QToolBar;
17+
m_tabs_container = new QTabWidget;
18+
m_tabs_container->setElideMode(Qt::TextElideMode::ElideRight);
19+
m_tabs_container->setMovable(true);
20+
m_tabs_container->setTabsClosable(true);
1321

14-
auto reload_icon_path = QString("%1/res/icons/16x16/reload.png").arg(s_serenity_resource_root.characters());
15-
auto* reload_action = new QAction(QIcon(reload_icon_path), "Reload");
16-
reload_action->setShortcut(QKeySequence("Ctrl+R"));
17-
m_toolbar->addAction(reload_action);
22+
auto menu = menuBar()->addMenu("File");
23+
auto new_tab_action = menu->addAction("New Tab", QKeySequence(Qt::CTRL | Qt::Key_T));
24+
auto quit_action = menu->addAction("Quit", QKeySequence(Qt::CTRL | Qt::Key_Q));
1825

19-
m_location_edit = new QLineEdit;
20-
m_toolbar->addWidget(m_location_edit);
26+
QObject::connect(new_tab_action, &QAction::triggered, this, &BrowserWindow::new_tab);
27+
QObject::connect(quit_action, &QAction::triggered, this, &QMainWindow::close);
28+
QObject::connect(m_tabs_container, &QTabWidget::currentChanged, [this](int index) {
29+
setWindowTitle(m_tabs_container->tabText(index));
30+
setWindowIcon(m_tabs_container->tabIcon(index));
31+
});
2132

22-
addToolBar(m_toolbar);
33+
new_tab();
34+
35+
setCentralWidget(m_tabs_container);
36+
}
2337

24-
m_view = new WebView;
25-
setCentralWidget(m_view);
38+
void BrowserWindow::new_tab()
39+
{
40+
auto tab = make<Tab>(this);
41+
auto tab_ptr = tab.ptr();
42+
m_tabs.append(std::move(tab));
2643

27-
QObject::connect(m_view, &WebView::linkHovered, statusBar(), &QStatusBar::showMessage);
28-
QObject::connect(m_view, &WebView::linkUnhovered, statusBar(), &QStatusBar::clearMessage);
44+
if (m_current_tab == nullptr) {
45+
m_current_tab = tab_ptr;
46+
}
2947

30-
QObject::connect(m_view, &WebView::loadStarted, m_location_edit, &QLineEdit::setText);
31-
QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &BrowserWindow::location_edit_return_pressed);
32-
QObject::connect(m_view, &WebView::title_changed, this, &BrowserWindow::page_title_changed);
33-
QObject::connect(m_view, &WebView::favicon_changed, this, &BrowserWindow::page_favicon_changed);
48+
m_tabs_container->addTab(tab_ptr, "New Tab");
3449

35-
QObject::connect(reload_action, &QAction::triggered, this, &BrowserWindow::reload);
50+
QObject::connect(tab_ptr, &Tab::title_changed, this, &BrowserWindow::tab_title_changed);
51+
QObject::connect(tab_ptr, &Tab::favicon_changed, this, &BrowserWindow::tab_favicon_changed);
3652
}
3753

38-
void BrowserWindow::location_edit_return_pressed()
54+
int BrowserWindow::tab_index(Tab* tab)
3955
{
40-
view().load(m_location_edit->text().toUtf8().data());
56+
return m_tabs_container->indexOf(tab);
4157
}
4258

43-
void BrowserWindow::page_title_changed(QString title)
59+
void BrowserWindow::tab_title_changed(int index, QString const& title)
4460
{
45-
if (title.isEmpty())
61+
if (title.isEmpty()) {
62+
m_tabs_container->setTabText(index, "...");
4663
setWindowTitle("Ladybird");
47-
else
64+
} else {
65+
m_tabs_container->setTabText(index, title);
4866
setWindowTitle(QString("%1 - Ladybird").arg(title));
67+
}
4968
}
5069

51-
void BrowserWindow::page_favicon_changed(QIcon icon)
70+
void BrowserWindow::tab_favicon_changed(int index, QIcon icon)
5271
{
72+
m_tabs_container->setTabIcon(index, icon);
5373
setWindowIcon(icon);
5474
}
5575

@@ -62,8 +82,3 @@ void BrowserWindow::closeEvent(QCloseEvent* event)
6282
// all of the browser windows have closed.
6383
m_event_loop.quit(0);
6484
}
65-
66-
void BrowserWindow::reload()
67-
{
68-
view().reload();
69-
}

Ladybird/BrowserWindow.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
1+
/*
2+
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include "Tab.h"
8+
#include <AK/Vector.h>
19
#include <LibCore/Forward.h>
210
#include <QIcon>
311
#include <QLineEdit>
412
#include <QMainWindow>
13+
#include <QMenuBar>
14+
#include <QTabWidget>
515
#include <QToolBar>
616

717
#pragma once
@@ -13,21 +23,21 @@ class BrowserWindow : public QMainWindow {
1323
public:
1424
explicit BrowserWindow(Core::EventLoop&);
1525

16-
WebView& view() { return *m_view; }
26+
WebView& view() const { return m_current_tab->view(); }
1727

18-
virtual void closeEvent(QCloseEvent*) override;
28+
int tab_index(Tab*);
1929

20-
public slots:
21-
void location_edit_return_pressed();
22-
void page_title_changed(QString);
23-
void page_favicon_changed(QIcon);
30+
virtual void closeEvent(QCloseEvent*) override;
2431

2532
public slots:
26-
void reload();
33+
void tab_title_changed(int index, QString const&);
34+
void tab_favicon_changed(int index, QIcon icon);
35+
void new_tab();
2736

2837
private:
29-
QToolBar* m_toolbar { nullptr };
30-
QLineEdit* m_location_edit { nullptr };
31-
WebView* m_view { nullptr };
38+
QTabWidget* m_tabs_container { nullptr };
39+
Vector<NonnullOwnPtr<Tab>> m_tabs;
40+
Tab* m_current_tab { nullptr };
41+
3242
Core::EventLoop& m_event_loop;
3343
};

Ladybird/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ set(SOURCES
3737
RequestManagerQt.cpp
3838
main.cpp
3939
WebView.cpp
40+
Tab.cpp
4041
)
4142

4243
add_executable(ladybird ${SOURCES})

Ladybird/Tab.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3+
* Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
4+
*
5+
* SPDX-License-Identifier: BSD-2-Clause
6+
*/
7+
8+
#include "Tab.h"
9+
#include "BrowserWindow.h"
10+
#include <QCoreApplication>
11+
#include <QStatusBar>
12+
#include <utility>
13+
14+
extern String s_serenity_resource_root;
15+
16+
Tab::Tab(QMainWindow* window)
17+
: m_window(window)
18+
{
19+
m_layout = new QBoxLayout(QBoxLayout::Direction::TopToBottom, this);
20+
m_layout->setContentsMargins(0, 0, 0, 0);
21+
22+
m_view = new WebView;
23+
m_toolbar = new QToolBar;
24+
m_location_edit = new QLineEdit;
25+
26+
m_layout->addWidget(m_toolbar);
27+
m_layout->addWidget(m_view);
28+
29+
auto reload_icon_path = QString("%1/res/icons/16x16/reload.png").arg(s_serenity_resource_root.characters());
30+
auto* reload_action = new QAction(QIcon(reload_icon_path), "Reload");
31+
reload_action->setShortcut(QKeySequence("Ctrl+R"));
32+
m_toolbar->addAction(reload_action);
33+
m_toolbar->addWidget(m_location_edit);
34+
35+
QObject::connect(m_view, &WebView::linkHovered, m_window->statusBar(), &QStatusBar::showMessage);
36+
QObject::connect(m_view, &WebView::linkUnhovered, m_window->statusBar(), &QStatusBar::clearMessage);
37+
38+
QObject::connect(m_view, &WebView::loadStarted, m_location_edit, &QLineEdit::setText);
39+
QObject::connect(m_location_edit, &QLineEdit::returnPressed, this, &Tab::location_edit_return_pressed);
40+
QObject::connect(m_view, &WebView::title_changed, this, &Tab::page_title_changed);
41+
QObject::connect(m_view, &WebView::favicon_changed, this, &Tab::page_favicon_changed);
42+
43+
QObject::connect(reload_action, &QAction::triggered, this, &Tab::reload);
44+
}
45+
46+
void Tab::reload()
47+
{
48+
view().reload();
49+
}
50+
51+
void Tab::location_edit_return_pressed()
52+
{
53+
view().load(m_location_edit->text().toUtf8().data());
54+
}
55+
56+
void Tab::page_title_changed(QString title)
57+
{
58+
emit title_changed(tab_index(), std::move(title));
59+
}
60+
61+
void Tab::page_favicon_changed(QIcon icon)
62+
{
63+
emit favicon_changed(tab_index(), std::move(icon));
64+
}
65+
66+
int Tab::tab_index()
67+
{
68+
// FIXME: I hear you like footguns...
69+
// There has to be a better way of doing this
70+
auto browser_window = reinterpret_cast<BrowserWindow*>(m_window);
71+
return browser_window->tab_index(this);
72+
}

Ladybird/Tab.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3+
* Copyright (c) 2022, Matthew Costa <ucosty@gmail.com>
4+
*
5+
* SPDX-License-Identifier: BSD-2-Clause
6+
*/
7+
8+
#pragma once
9+
10+
#include "WebView.h"
11+
#include <QBoxLayout>
12+
#include <QLineEdit>
13+
#include <QToolBar>
14+
#include <QWidget>
15+
16+
class Tab final : public QWidget {
17+
Q_OBJECT
18+
public:
19+
explicit Tab(QMainWindow* window);
20+
21+
WebView& view() { return *m_view; }
22+
23+
public slots:
24+
void location_edit_return_pressed();
25+
void page_title_changed(QString);
26+
void page_favicon_changed(QIcon);
27+
void reload();
28+
29+
signals:
30+
void title_changed(int id, QString);
31+
void favicon_changed(int id, QIcon);
32+
33+
private:
34+
QBoxLayout* m_layout;
35+
QToolBar* m_toolbar { nullptr };
36+
QLineEdit* m_location_edit { nullptr };
37+
WebView* m_view { nullptr };
38+
QMainWindow* m_window { nullptr };
39+
40+
int tab_index();
41+
};

0 commit comments

Comments
 (0)