UI/Qt: Add optional rounded window corners on macOS

Add an advanced setting for rounded browser window corners. On macOS,
Qt browser windows now use a layer-backed corner radius, and clear it
when maximized, fullscreen, or the setting is disabled.

Hide the setting from about:settings on other platforms for now since Qt
does not expose a native antialiased window corner API there.
This commit is contained in:
Andreas Kling 2026-06-01 02:48:01 +02:00 committed by Andreas Kling
parent c4e8817246
commit 01973d1e5c
8 changed files with 115 additions and 1 deletions

View file

@ -89,6 +89,14 @@ static Array<ConfigVariableDefinition, static_cast<size_t>(ConfigVariableID::Cou
.default_value = JsonArray {},
.array_element_type = JsonValue::Type::String,
},
{
.id = ConfigVariableID::UseRoundedWindowCorners,
.name = "ui.window.use_rounded_corners"sv,
.title = "Use rounded window corners"sv,
.description = "Clip browser windows to rounded corners."sv,
.default_value = true,
.array_element_type = {},
},
} };
ReadonlySpan<ConfigVariableDefinition const> config_variable_definitions()

View file

@ -52,6 +52,7 @@ enum class ConfigVariableID : u8 {
ShowWebContentProcessIDInTabTitle,
ShowAdvancedDebugMenu,
ContentBlockerListPaths,
UseRoundedWindowCorners,
Count,
};

View file

@ -5,6 +5,7 @@
*/
#include <AK/JsonArray.h>
#include <AK/Platform.h>
#include <LibURL/Parser.h>
#include <LibWebView/Application.h>
#include <LibWebView/SearchEngine.h>
@ -32,6 +33,16 @@ static StringView config_variable_type_to_string(JsonValue::Type type)
VERIFY_NOT_REACHED();
}
static bool should_show_config_variable([[maybe_unused]] ConfigVariableID id)
{
#if !defined(AK_OS_MACOS)
if (id == ConfigVariableID::UseRoundedWindowCorners)
return false;
#endif
return true;
}
void SettingsUI::register_interfaces()
{
register_interface("loadFeatures"sv, [this](auto const&) {
@ -127,6 +138,9 @@ void SettingsUI::load_current_settings()
JsonArray config_variables;
for (auto const& variable : config_variable_definitions()) {
if (!should_show_config_variable(variable.id))
continue;
JsonObject variable_object;
variable_object.set("name"sv, variable.name);
variable_object.set("title"sv, variable.title);

View file

@ -10,6 +10,7 @@
*/
#include <AK/HashMap.h>
#include <AK/Platform.h>
#include <AK/RefPtr.h>
#include <AK/StdLibExtras.h>
#include <AK/TypeCasts.h>
@ -19,6 +20,9 @@
#include <UI/Qt/BrowserWindow.h>
#include <UI/Qt/ChromeStyle.h>
#include <UI/Qt/Icon.h>
#if defined(AK_OS_MACOS)
# include <UI/Qt/MacWindow.h>
#endif
#include <UI/Qt/Menu.h>
#include <UI/Qt/Settings.h>
#include <UI/Qt/StringUtils.h>
@ -55,6 +59,9 @@ namespace Ladybird {
static constexpr auto AUDIO_STATE_BUTTON_POSITION = QTabBar::LeftSide;
static constexpr auto TAB_CLOSE_BUTTON_POSITION = QTabBar::RightSide;
static constexpr auto WINDOW_DRAG_REGION_PROPERTY = "LadybirdWindowDragRegion";
#if defined(AK_OS_MACOS)
static constexpr qreal WINDOW_CORNER_RADIUS = 12.0;
#endif
static bool should_use_screen_signal_for_dpi_changes()
{
@ -227,6 +234,7 @@ BrowserWindow::BrowserWindow(Vector<URL::URL> const& initial_urls, IsPopupWindow
setAttribute(Qt::WA_OpaquePaintEvent);
setWindowIcon(app_icon());
qApp->installEventFilter(this);
update_window_corners();
update_tabs_display();
@ -1229,6 +1237,7 @@ void BrowserWindow::clear_resize_cursor()
void BrowserWindow::resizeEvent(QResizeEvent* event)
{
QWidget::resizeEvent(event);
update_window_corners();
for_each_tab([&](auto& tab) {
tab.view().set_window_size({ width(), height() });
@ -1244,6 +1253,7 @@ void BrowserWindow::changeEvent(QEvent* event)
} else if (event->type() == QEvent::WindowStateChange) {
update_menu_bar_window_control_icons();
m_tabs_container->update_window_button_icons();
update_window_corners();
QWindowStateChangeEvent* stateChangeEvent = static_cast<QWindowStateChangeEvent*>(event);
bool was_fullscreen = stateChangeEvent->oldState() & Qt::WindowFullScreen;
@ -1260,6 +1270,26 @@ void BrowserWindow::changeEvent(QEvent* event)
QWidget::changeEvent(event);
}
void BrowserWindow::config_variable_changed(WebView::ConfigVariableID variable)
{
if (variable == WebView::ConfigVariableID::UseRoundedWindowCorners)
update_window_corners();
}
void BrowserWindow::update_window_corners()
{
auto should_use_rounded_corners = WebView::Application::settings().config_variable_as_bool(WebView::ConfigVariableID::UseRoundedWindowCorners);
auto should_round_window = should_use_rounded_corners && !isMaximized() && !isFullScreen();
#if defined(AK_OS_MACOS)
clearMask();
set_rounded_window_corners(*this, should_round_window, WINDOW_CORNER_RADIUS);
#else
clearMask();
(void)should_round_window;
#endif
}
void BrowserWindow::moveEvent(QMoveEvent* event)
{
QWidget::moveEvent(event);

View file

@ -11,6 +11,7 @@
#include <LibWeb/HTML/ActivateTab.h>
#include <LibWeb/HTML/AudioPlayState.h>
#include <LibWebView/Forward.h>
#include <LibWebView/Settings.h>
#include <UI/Qt/Tab.h>
#include <UI/Qt/TabBar.h>
@ -80,7 +81,9 @@ private:
bool m_debounce { false };
};
class BrowserWindow : public QMainWindow {
class BrowserWindow
: public QMainWindow
, public WebView::SettingsObserver {
Q_OBJECT
public:
@ -151,6 +154,7 @@ private:
virtual void moveEvent(QMoveEvent*) override;
virtual void wheelEvent(QWheelEvent*) override;
virtual void closeEvent(QCloseEvent*) override;
virtual void config_variable_changed(WebView::ConfigVariableID) override;
Tab& create_new_tab(Web::HTML::ActivateTab, Tab& parent, Optional<u64> page_index);
void initialize_tab(Tab*);
@ -161,6 +165,7 @@ private:
Optional<Qt::CursorShape> resize_cursor_for_edges(Qt::Edges) const;
void update_resize_cursor(QPoint const&);
void clear_resize_cursor();
void update_window_corners();
template<typename Callback>
void for_each_tab(Callback&& callback)

View file

@ -25,6 +25,12 @@ target_sources(ladybird PRIVATE
ladybird.qrc
)
target_link_libraries(ladybird PRIVATE Qt::Core Qt::Gui Qt::Widgets)
if (APPLE)
target_sources(ladybird PRIVATE MacWindow.mm)
target_link_libraries(ladybird PRIVATE "-framework Cocoa" "-framework QuartzCore")
endif()
create_ladybird_bundle(ladybird)
if (WIN32)

15
UI/Qt/MacWindow.h Normal file
View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
class QWidget;
namespace Ladybird {
void set_rounded_window_corners(QWidget&, bool enabled, double radius);
}

35
UI/Qt/MacWindow.mm Normal file
View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2026-present, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <UI/Qt/MacWindow.h>
#include <QWidget>
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
namespace Ladybird {
void set_rounded_window_corners(QWidget& widget, bool enabled, double radius)
{
auto* view = reinterpret_cast<NSView*>(widget.winId());
if (!view)
return;
auto* window = view.window;
if (!window)
return;
auto* content_view = window.contentView;
if (!content_view)
return;
content_view.wantsLayer = YES;
content_view.layer.cornerRadius = enabled ? radius : 0.0;
content_view.layer.masksToBounds = enabled ? YES : NO;
}
}