mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 20:15:17 +00:00
UI/Qt: Add interface for exiting fullscreen
Added an "exit fullscreen" button that displays when entering fullscreen. If the user switches tab while in fullscreen, ladybird will exit fullscreen and restore the window to the previous state. Exiting fullscreen can be done in one of two ways: - Click the exit fullscreen button - Press escape The exit fullscreen button will disappear after a certain amount of time after entering fullscreen. To make it appear again, move the mouse cursor to the top of the screen.
This commit is contained in:
parent
d79098939d
commit
0cc3c78181
3 changed files with 209 additions and 3 deletions
|
@ -4,10 +4,12 @@
|
|||
* Copyright (c) 2022, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
|
||||
* Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Sam Atkins <sam@ladybird.org>
|
||||
* Copyright (c) 2025, Simon Farre <simon.farre.cx@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibWeb/CSS/PreferredColorScheme.h>
|
||||
#include <LibWeb/CSS/PreferredContrast.h>
|
||||
|
@ -32,12 +34,133 @@
|
|||
#include <QInputDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QPushButton>
|
||||
#include <QShortcut>
|
||||
#include <QStatusBar>
|
||||
#include <QTabBar>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
#include <qnamespace.h>
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
FullscreenMode::FullscreenMode(BrowserWindow* window, ExitFullscreenButton* exit_button)
|
||||
: QObject(window)
|
||||
, m_window(window)
|
||||
, m_exit_button(exit_button)
|
||||
{
|
||||
connect(m_exit_button, &QPushButton::clicked, this, [this]() {
|
||||
exit();
|
||||
});
|
||||
}
|
||||
|
||||
void FullscreenMode::exit()
|
||||
{
|
||||
// If there's a document tree in fullscreen, exit fully on root document.
|
||||
if (is_api_fullscreen()) {
|
||||
qApp->removeEventFilter(this);
|
||||
if (m_window->tab_index(m_fullscreen_tab) != -1) {
|
||||
m_fullscreen_tab->view().exit_fullscreen();
|
||||
}
|
||||
emit on_exit_fullscreen();
|
||||
}
|
||||
m_fullscreen_tab = nullptr;
|
||||
}
|
||||
|
||||
void FullscreenMode::enter(Tab* tab)
|
||||
{
|
||||
qApp->installEventFilter(this);
|
||||
m_fullscreen_tab = tab;
|
||||
m_window->enter_fullscreen();
|
||||
}
|
||||
|
||||
void FullscreenMode::entered_fullscreen()
|
||||
{
|
||||
m_debounce = true;
|
||||
m_exit_button->animate_show();
|
||||
// Let button float in place 3 * time it takes to animate it in place
|
||||
QTimer::singleShot(button_animation_time() * 3, [this]() { m_debounce = false; });
|
||||
}
|
||||
|
||||
bool FullscreenMode::is_api_fullscreen() const
|
||||
{
|
||||
return m_fullscreen_tab;
|
||||
}
|
||||
|
||||
bool FullscreenMode::debounce() const
|
||||
{
|
||||
return m_debounce;
|
||||
}
|
||||
|
||||
void FullscreenMode::maybe_animate_show_exit_button(QPointF pos)
|
||||
{
|
||||
u64 const mouse_y = static_cast<u64>(pos.y());
|
||||
u64 const threshold = static_cast<u64>(m_window->height() * 0.01);
|
||||
|
||||
if (debounce()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Display the button if the mouse is 1% from the top
|
||||
if (mouse_y <= threshold) {
|
||||
if (!m_exit_button->isVisible()) {
|
||||
m_debounce = true;
|
||||
m_exit_button->animate_show();
|
||||
QTimer::singleShot(button_animation_time() * 3, [this]() { m_debounce = false; });
|
||||
}
|
||||
} else if (mouse_y > (threshold * 10) && m_exit_button->isVisible()) {
|
||||
// if the button has floated in, we want to hide it when leaving the top 10%
|
||||
m_exit_button->hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool FullscreenMode::eventFilter(QObject* obj, QEvent* event)
|
||||
{
|
||||
ASSERT(is_api_fullscreen());
|
||||
if (event->type() == QEvent::MouseMove) {
|
||||
QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
|
||||
maybe_animate_show_exit_button(mouse_event->pos());
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* key = static_cast<QKeyEvent*>(event);
|
||||
if (key->key() == Qt::Key_Escape)
|
||||
exit();
|
||||
}
|
||||
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
ExitFullscreenButton::ExitFullscreenButton(QWidget* parent)
|
||||
: QPushButton("Exit fullscreen", parent)
|
||||
{
|
||||
setStyleSheet("background-color:rgb(55, 99, 129); color: white; padding: 10px; border-radius: 5px;");
|
||||
adjustSize();
|
||||
hide();
|
||||
m_widget_animation = new QPropertyAnimation(this, "pos");
|
||||
}
|
||||
|
||||
void ExitFullscreenButton::animate_show()
|
||||
{
|
||||
if (isVisible())
|
||||
return;
|
||||
|
||||
show();
|
||||
QScreen* current_screen = screen();
|
||||
QRect screen_geometry = current_screen->geometry();
|
||||
|
||||
int const destination_x = (screen_geometry.width() - width()) / 2;
|
||||
int const destination_y = static_cast<int>(static_cast<float>(screen_geometry.height()) * 0.05);
|
||||
|
||||
m_widget_animation->setDuration(FullscreenMode::button_animation_time());
|
||||
m_widget_animation->setStartValue(QPoint(destination_x, -height()));
|
||||
m_widget_animation->setEndValue(QPoint(destination_x, destination_y));
|
||||
m_widget_animation->setEasingCurve(QEasingCurve::OutBounce);
|
||||
m_widget_animation->start();
|
||||
}
|
||||
|
||||
static QIcon const& app_icon()
|
||||
{
|
||||
static QIcon icon;
|
||||
|
@ -618,12 +741,19 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, IsPopupWindow
|
|||
(void)static_cast<Ladybird::Application*>(QApplication::instance())->new_window({});
|
||||
});
|
||||
QObject::connect(open_file_action, &QAction::triggered, this, &BrowserWindow::open_file);
|
||||
|
||||
m_exit_button = new ExitFullscreenButton { this };
|
||||
m_fullscreen_mode = new FullscreenMode { this, m_exit_button };
|
||||
connect(m_fullscreen_mode, &FullscreenMode::on_exit_fullscreen, this, &BrowserWindow::exit_fullscreen);
|
||||
connect(m_fullscreen_mode, &FullscreenMode::on_exit_fullscreen, m_exit_button, &ExitFullscreenButton::hide);
|
||||
|
||||
QObject::connect(m_tabs_container, &QTabWidget::currentChanged, [this](int index) {
|
||||
auto* tab = as<Tab>(m_tabs_container->widget(index));
|
||||
if (tab)
|
||||
setWindowTitle(QString("%1 - Ladybird").arg(tab->title()));
|
||||
|
||||
set_current_tab(tab);
|
||||
fullscreen_mode().exit();
|
||||
});
|
||||
QObject::connect(m_tabs_container, &QTabWidget::tabCloseRequested, this, &BrowserWindow::close_tab);
|
||||
QObject::connect(close_current_tab_action, &QAction::triggered, this, &BrowserWindow::close_current_tab);
|
||||
|
@ -762,6 +892,11 @@ Tab& BrowserWindow::create_new_tab(Web::HTML::ActivateTab activate_tab, Tab& par
|
|||
return *tab;
|
||||
}
|
||||
|
||||
FullscreenMode& BrowserWindow::fullscreen_mode()
|
||||
{
|
||||
return *m_fullscreen_mode;
|
||||
}
|
||||
|
||||
Tab& BrowserWindow::create_new_tab(Web::HTML::ActivateTab activate_tab)
|
||||
{
|
||||
auto* tab = new Tab(this);
|
||||
|
@ -1161,6 +1296,7 @@ void BrowserWindow::enter_fullscreen()
|
|||
{
|
||||
m_tabs_container->tabBar()->hide();
|
||||
m_tabs_container->cornerWidget()->hide();
|
||||
m_restore_to_maximized = isMaximized();
|
||||
showFullScreen();
|
||||
}
|
||||
|
||||
|
@ -1168,7 +1304,10 @@ void BrowserWindow::exit_fullscreen()
|
|||
{
|
||||
m_tabs_container->tabBar()->show();
|
||||
m_tabs_container->cornerWidget()->show();
|
||||
showNormal();
|
||||
if (m_restore_to_maximized)
|
||||
showMaximized();
|
||||
else
|
||||
showNormal();
|
||||
}
|
||||
|
||||
bool BrowserWindow::event(QEvent* event)
|
||||
|
@ -1195,6 +1334,17 @@ void BrowserWindow::resizeEvent(QResizeEvent* event)
|
|||
});
|
||||
}
|
||||
|
||||
void BrowserWindow::changeEvent(QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::WindowStateChange) {
|
||||
QWindowStateChangeEvent* stateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
|
||||
if (windowState() & Qt::WindowFullScreen && !(stateChangeEvent->oldState() & Qt::WindowFullScreen)) {
|
||||
m_fullscreen_mode->entered_fullscreen();
|
||||
}
|
||||
}
|
||||
QWidget::changeEvent(event);
|
||||
}
|
||||
|
||||
void BrowserWindow::moveEvent(QMoveEvent* event)
|
||||
{
|
||||
QWidget::moveEvent(event);
|
||||
|
|
|
@ -18,13 +18,62 @@
|
|||
#include <QLineEdit>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QPushButton>
|
||||
#include <QTabBar>
|
||||
#include <QTabWidget>
|
||||
#include <QToolBar>
|
||||
|
||||
class QPropertyAnimation;
|
||||
|
||||
namespace Ladybird {
|
||||
|
||||
class WebContentView;
|
||||
class BrowserWindow;
|
||||
|
||||
class ExitFullscreenButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ExitFullscreenButton(QWidget* parent = nullptr);
|
||||
~ExitFullscreenButton() override = default;
|
||||
void animate_show();
|
||||
|
||||
private:
|
||||
QPropertyAnimation* m_widget_animation;
|
||||
};
|
||||
|
||||
// Handles Qt UI state related to when Ladybird has entered fullscreen,
|
||||
// like displaying an exit button, listening for escape key presses and so on
|
||||
class FullscreenMode : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static constexpr int button_animation_time() { return 750; }
|
||||
explicit FullscreenMode(BrowserWindow* window, ExitFullscreenButton* exit_button);
|
||||
|
||||
void exit();
|
||||
void enter(Tab* tab);
|
||||
// Called after a window change event that has identifed the current window state to be fullscreen.
|
||||
void entered_fullscreen();
|
||||
bool is_api_fullscreen() const;
|
||||
signals:
|
||||
void on_exit_fullscreen();
|
||||
|
||||
protected:
|
||||
// FullscreenMode's eventFilter is responsible for things that need to happen when a document
|
||||
// is in "fullscreen API fullscreen", like exiting when pushing escape, showing the exit button
|
||||
virtual bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
|
||||
private:
|
||||
bool debounce() const;
|
||||
// Called when in fullscreen. Displays exit fullscreen button if mouse comes close to the top of the screen.
|
||||
void maybe_animate_show_exit_button(QPointF pos);
|
||||
BrowserWindow* m_window;
|
||||
ExitFullscreenButton* m_exit_button;
|
||||
// Never access this directly. First check m_window->tab_index(m_fullscreen_tab) != -1, to verify it's liveness.
|
||||
Tab* m_fullscreen_tab { nullptr };
|
||||
bool m_debounce { false };
|
||||
};
|
||||
|
||||
class BrowserWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
@ -42,6 +91,7 @@ public:
|
|||
int tab_count() { return m_tabs_container->count(); }
|
||||
int tab_index(Tab*);
|
||||
Tab& create_new_tab(Web::HTML::ActivateTab activate_tab);
|
||||
FullscreenMode& fullscreen_mode();
|
||||
|
||||
QMenu& hamburger_menu()
|
||||
{
|
||||
|
@ -141,6 +191,7 @@ protected:
|
|||
private:
|
||||
virtual bool event(QEvent*) override;
|
||||
virtual void resizeEvent(QResizeEvent*) override;
|
||||
virtual void changeEvent(QEvent* event) override;
|
||||
virtual void moveEvent(QMoveEvent*) override;
|
||||
virtual void wheelEvent(QWheelEvent*) override;
|
||||
virtual void closeEvent(QCloseEvent*) override;
|
||||
|
@ -212,6 +263,11 @@ private:
|
|||
ByteString m_navigator_compatibility_mode {};
|
||||
|
||||
IsPopupWindow m_is_popup_window { IsPopupWindow::No };
|
||||
|
||||
ExitFullscreenButton* m_exit_button { nullptr };
|
||||
FullscreenMode* m_fullscreen_mode { nullptr };
|
||||
// Determine if window should restore to maximized or normal, when exiting fullscreen.
|
||||
bool m_restore_to_maximized { false };
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -367,13 +367,13 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
|
|||
view().on_fullscreen_window = [this]() {
|
||||
BrowserWindow* window = static_cast<BrowserWindow*>(m_window);
|
||||
m_toolbar->hide();
|
||||
window->enter_fullscreen();
|
||||
window->fullscreen_mode().enter(this);
|
||||
view().did_update_window_rect();
|
||||
};
|
||||
|
||||
view().on_exit_fullscreen_window = [this]() {
|
||||
BrowserWindow* window = static_cast<BrowserWindow*>(m_window);
|
||||
window->exit_fullscreen();
|
||||
window->fullscreen_mode().exit();
|
||||
m_toolbar->show();
|
||||
view().did_update_window_rect();
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue