diff --git a/Ladybird/CMakeLists.txt b/Ladybird/CMakeLists.txt index ccc78718fff..ce3ccf87700 100644 --- a/Ladybird/CMakeLists.txt +++ b/Ladybird/CMakeLists.txt @@ -37,6 +37,7 @@ set(SOURCES RequestManagerQt.cpp main.cpp WebView.cpp + History.cpp Tab.cpp ) diff --git a/Ladybird/History.cpp b/Ladybird/History.cpp new file mode 100644 index 00000000000..64e66633a9d --- /dev/null +++ b/Ladybird/History.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "History.h" + +namespace Browser { + +void History::dump() const +{ + dbgln("Dump {} items(s)", m_items.size()); + int i = 0; + for (auto& item : m_items) { + dbgln("[{}] {} '{}' {}", i, item.url, item.title, m_current == i ? '*' : ' '); + ++i; + } +} + +void History::push(const URL& url, String const& title) +{ + if (!m_items.is_empty() && m_items[m_current].url == url) + return; + m_items.shrink(m_current + 1); + m_items.append(URLTitlePair { + .url = url, + .title = title, + }); + m_current++; +} + +History::URLTitlePair History::current() const +{ + if (m_current == -1) + return {}; + return m_items[m_current]; +} + +void History::go_back(int steps) +{ + VERIFY(can_go_back(steps)); + m_current -= steps; +} + +void History::go_forward(int steps) +{ + VERIFY(can_go_forward(steps)); + m_current += steps; +} + +void History::clear() +{ + m_items = {}; + m_current = -1; +} + +void History::update_title(String const& title) +{ + m_items[m_current].title = title; +} + +Vector History::get_back_title_history() +{ + Vector back_title_history; + for (int i = m_current - 1; i >= 0; i--) { + back_title_history.append(m_items[i].title); + } + return back_title_history; +} + +Vector History::get_forward_title_history() +{ + Vector forward_title_history; + for (int i = m_current + 1; i < static_cast(m_items.size()); i++) { + forward_title_history.append(m_items[i].title); + } + return forward_title_history; +} + +} diff --git a/Ladybird/History.h b/Ladybird/History.h new file mode 100644 index 00000000000..4449944c727 --- /dev/null +++ b/Ladybird/History.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Browser { + +class History { +public: + struct URLTitlePair { + URL url; + String title; + }; + void dump() const; + + void push(const URL& url, String const& title); + void update_title(String const& title); + URLTitlePair current() const; + + Vector get_back_title_history(); + Vector get_forward_title_history(); + + void go_back(int steps = 1); + void go_forward(int steps = 1); + + bool can_go_back(int steps = 1) { return (m_current - steps) >= 0; } + bool can_go_forward(int steps = 1) { return (m_current + steps) < static_cast(m_items.size()); } + void clear(); + +private: + Vector m_items; + int m_current { -1 }; +}; + +} diff --git a/Ladybird/Tab.cpp b/Ladybird/Tab.cpp index 906e059173c..75f823894c5 100644 --- a/Ladybird/Tab.cpp +++ b/Ladybird/Tab.cpp @@ -7,9 +7,9 @@ #include "Tab.h" #include "BrowserWindow.h" +#include "History.h" #include #include -#include extern String s_serenity_resource_root; @@ -26,21 +26,67 @@ Tab::Tab(QMainWindow* window) m_layout->addWidget(m_toolbar); m_layout->addWidget(m_view); + auto back_icon_path = QString("%1/res/icons/16x16/go-back.png").arg(s_serenity_resource_root.characters()); + auto forward_icon_path = QString("%1/res/icons/16x16/go-forward.png").arg(s_serenity_resource_root.characters()); + auto home_icon_path = QString("%1/res/icons/16x16/go-home.png").arg(s_serenity_resource_root.characters()); 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_back_action = make(QIcon(back_icon_path), "Back"); + m_back_action->setShortcut(QKeySequence("Alt+Left")); + m_forward_action = make(QIcon(forward_icon_path), "Forward"); + m_forward_action->setShortcut(QKeySequence("Alt+Right")); + m_home_action = make(QIcon(home_icon_path), "Home"); + m_reload_action = make(QIcon(reload_icon_path), "Reload"); + m_reload_action->setShortcut(QKeySequence("Ctrl+R")); + + m_toolbar->addAction(m_back_action); + m_toolbar->addAction(m_forward_action); + m_toolbar->addAction(m_reload_action); + m_toolbar->addAction(m_home_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_view, &WebView::loadStarted, [this](const URL& url) { + m_location_edit->setText(url.to_string().characters()); + m_history.push(url, m_title.toUtf8().data()); + }); 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); + QObject::connect(m_back_action, &QAction::triggered, this, &Tab::back); + QObject::connect(m_forward_action, &QAction::triggered, this, &Tab::forward); + QObject::connect(m_home_action, &QAction::triggered, this, &Tab::home); + QObject::connect(m_reload_action, &QAction::triggered, this, &Tab::reload); +} + +void Tab::navigate(QString const& url) +{ + view().load(url.toUtf8().data()); +} + +void Tab::back() +{ + if (!m_history.can_go_back()) + return; + + m_history.go_back(); + view().load(m_history.current().url.to_string()); +} + +void Tab::forward() +{ + if (!m_history.can_go_forward()) + return; + + m_history.go_forward(); + view().load(m_history.current().url.to_string()); +} + +void Tab::home() +{ + navigate("https://www.serenityos.org/"); } void Tab::reload() @@ -50,11 +96,12 @@ void Tab::reload() void Tab::location_edit_return_pressed() { - view().load(m_location_edit->text().toUtf8().data()); + navigate(m_location_edit->text()); } void Tab::page_title_changed(QString title) { + m_title = title; emit title_changed(tab_index(), std::move(title)); } diff --git a/Ladybird/Tab.h b/Ladybird/Tab.h index 9e01c9e4c0b..511715232d4 100644 --- a/Ladybird/Tab.h +++ b/Ladybird/Tab.h @@ -7,6 +7,9 @@ #pragma once +#define AK_DONT_REPLACE_STD + +#include "History.h" #include "WebView.h" #include #include @@ -20,10 +23,15 @@ public: WebView& view() { return *m_view; } + void navigate(QString const&); + public slots: void location_edit_return_pressed(); void page_title_changed(QString); void page_favicon_changed(QIcon); + void back(); + void forward(); + void home(); void reload(); signals: @@ -36,6 +44,13 @@ private: QLineEdit* m_location_edit { nullptr }; WebView* m_view { nullptr }; QMainWindow* m_window { nullptr }; + Browser::History m_history; + QString m_title; + + OwnPtr m_back_action; + OwnPtr m_forward_action; + OwnPtr m_home_action; + OwnPtr m_reload_action; int tab_index(); }; diff --git a/Ladybird/WebView.cpp b/Ladybird/WebView.cpp index 1a66cce55d1..bd6ce779dff 100644 --- a/Ladybird/WebView.cpp +++ b/Ladybird/WebView.cpp @@ -148,7 +148,7 @@ public: virtual void page_did_start_loading(AK::URL const& url) override { - emit m_view.loadStarted(url.to_string().characters()); + emit m_view.loadStarted(url); } virtual void page_did_finish_loading(AK::URL const&) override diff --git a/Ladybird/WebView.h b/Ladybird/WebView.h index a38e7e165f9..c4a542cdb28 100644 --- a/Ladybird/WebView.h +++ b/Ladybird/WebView.h @@ -33,7 +33,7 @@ public: signals: void linkHovered(QString, int timeout = 0); void linkUnhovered(); - void loadStarted(QString); + void loadStarted(const URL&); void title_changed(QString); void favicon_changed(QIcon);