views: mac: Support dynamic resize of title bar height (see #3189)

This is intended for usage with frameless windows that show the standard window
buttons, where resizing the title bar height changes the button offset. Returning a
different value from CefWindowDelegate::GetTitlebarHeight and forcing a resize of
the NSWindow's theme frame (see ViewsWindow::NudgeWindow) will update the
title bar height.

To test:
1. Run `cefclient --use-views --hide-frame --show-window-buttons --url=http://tests/window`
2. Enter a new value for title bar height and click the "Set Titlebar Height" button
This commit is contained in:
Nik Pavlov 2023-03-16 17:19:50 +00:00 committed by Marshall Greenblatt
parent 0a2c7a1070
commit c83b3cda24
19 changed files with 156 additions and 62 deletions

View File

@ -393,6 +393,7 @@
'tests/cefclient/browser/temp_window_mac.mm',
'tests/cefclient/browser/text_input_client_osr_mac.h',
'tests/cefclient/browser/text_input_client_osr_mac.mm',
'tests/cefclient/browser/views_window_mac.mm',
'tests/cefclient/browser/window_test_runner_mac.h',
'tests/cefclient/browser/window_test_runner_mac.mm',
'tests/cefclient/cefclient_mac.mm',

View File

@ -6,15 +6,18 @@
#define CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_
#pragma once
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "include/internal/cef_ptr.h"
#include "ui/views/widget/native_widget_mac.h"
class CefWindow;
class CefWindowDelegate;
class CefNativeWidgetMac : public views::NativeWidgetMac {
public:
CefNativeWidgetMac(views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height);
CefRefPtr<CefWindow> window,
CefWindowDelegate* window_delegate);
~CefNativeWidgetMac() override = default;
CefNativeWidgetMac(const CefNativeWidgetMac&) = delete;
@ -29,9 +32,8 @@ class CefNativeWidgetMac : public views::NativeWidgetMac {
float* titlebar_height) override;
private:
const bool is_frameless_;
const bool with_window_buttons_;
const absl::optional<float> title_bar_height_;
const CefRefPtr<CefWindow> window_;
CefWindowDelegate* const window_delegate_;
};
#endif // CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_

View File

@ -4,17 +4,17 @@
#include "libcef/browser/views/native_widget_mac.h"
#include "include/views/cef_window.h"
#include "include/views/cef_window_delegate.h"
#include "libcef/browser/views/ns_window.h"
CefNativeWidgetMac::CefNativeWidgetMac(
views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height)
CefRefPtr<CefWindow> window,
CefWindowDelegate* window_delegate)
: views::NativeWidgetMac(delegate),
is_frameless_(is_frameless),
with_window_buttons_(with_window_buttons),
title_bar_height_(title_bar_height) {}
window_(window),
window_delegate_(window_delegate) {}
NativeWidgetMacNSWindow* CefNativeWidgetMac::CreateNSWindow(
const remote_cocoa::mojom::CreateWindowParams* params) {
@ -22,15 +22,18 @@ NativeWidgetMacNSWindow* CefNativeWidgetMac::CreateNSWindow(
NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable |
NSWindowStyleMaskClosable | NSWindowStyleMaskResizable |
NSWindowStyleMaskTexturedBackground;
auto window = [[CefNSWindow alloc] initWithStyle:style_mask
isFrameless:is_frameless_];
if (is_frameless_) {
bool is_frameless = window_delegate_->IsFrameless(window_);
auto window = [[CefNSWindow alloc] initWithStyle:style_mask
isFrameless:is_frameless];
if (is_frameless) {
[window setTitlebarAppearsTransparent:YES];
[window setTitleVisibility:NSWindowTitleHidden];
}
if (!with_window_buttons_) {
if (!window_delegate_->WithStandardWindowButtons(window_)) {
[[window standardWindowButton:NSWindowCloseButton] setHidden:YES];
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
@ -42,9 +45,8 @@ NativeWidgetMacNSWindow* CefNativeWidgetMac::CreateNSWindow(
void CefNativeWidgetMac::GetWindowFrameTitlebarHeight(
bool* override_titlebar_height,
float* titlebar_height) {
if (title_bar_height_) {
if (window_delegate_->GetTitlebarHeight(window_, titlebar_height)) {
*override_titlebar_height = true;
*titlebar_height = title_bar_height_.value();
} else {
views::NativeWidgetMac::GetWindowFrameTitlebarHeight(
override_titlebar_height, titlebar_height);

View File

@ -9,7 +9,6 @@
#include "include/views/cef_view.h"
#include "include/views/cef_window.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/views/view.h"
@ -29,6 +28,8 @@ class NativeWidgetDelegate;
}
} // namespace views
class CefWindowDelegate;
#define CEF_REQUIRE_VALID_RETURN(ret) \
if (!ParentClass::IsValid()) \
return ret;
@ -148,9 +149,8 @@ CefWindowHandle GetWindowHandle(gfx::NativeWindow window);
views::NativeWidget* CreateNativeWidget(
views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height);
CefRefPtr<CefWindow> window,
CefWindowDelegate* window_delegate);
} // namespace view_util

View File

@ -44,9 +44,8 @@ CefWindowHandle GetWindowHandle(gfx::NativeWindow window) {
views::NativeWidget* CreateNativeWidget(
views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height) {
CefRefPtr<CefWindow> window,
CefWindowDelegate* window_delegate) {
return nullptr;
}

View File

@ -47,10 +47,8 @@ CefWindowHandle GetWindowHandle(gfx::NativeWindow window) {
views::NativeWidget* CreateNativeWidget(
views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height) {
return new CefNativeWidgetMac(delegate, is_frameless, with_window_buttons,
title_bar_height);
CefRefPtr<CefWindow> window,
CefWindowDelegate* window_delegate) {
return new CefNativeWidgetMac(delegate, window, window_delegate);
}
} // namespace view_util

View File

@ -313,13 +313,8 @@ void CefWindowView::CreateWidget(gfx::AcceleratedWidget parent_widget) {
} else {
is_frameless_ = cef_delegate()->IsFrameless(cef_window);
const bool with_standard_buttons =
cef_delegate()->WithStandardWindowButtons(cef_window);
const auto title_bar_height = GetTitlebarHeight(cef_window);
params.native_widget = view_util::CreateNativeWidget(
widget, is_frameless_, with_standard_buttons, title_bar_height);
params.native_widget =
view_util::CreateNativeWidget(widget, cef_window, cef_delegate());
can_resize = cef_delegate()->CanResize(cef_window);
@ -666,14 +661,3 @@ views::NonClientFrameView* CefWindowView::GetNonClientFrameView() const {
}
return widget->non_client_view()->frame_view();
}
absl::optional<float> CefWindowView::GetTitlebarHeight(
const CefRefPtr<CefWindow>& window) const {
float title_bar_height = 0;
const bool has_title_bar_height =
cef_delegate()->GetTitlebarHeight(window, &title_bar_height);
if (has_title_bar_height) {
return title_bar_height;
}
return absl::nullopt;
}

View File

@ -14,7 +14,6 @@
#include "libcef/browser/views/overlay_view_host.h"
#include "libcef/browser/views/panel_view.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "ui/display/display.h"
#include "ui/views/widget/widget_delegate.h"
@ -120,9 +119,6 @@ class CefWindowView
void MoveOverlaysIfNecessary();
absl::optional<float> GetTitlebarHeight(
const CefRefPtr<CefWindow>& window) const;
// Not owned by this object.
Delegate* window_delegate_;

View File

@ -28,6 +28,12 @@ RootWindowViews::~RootWindowViews() {
REQUIRE_MAIN_THREAD();
}
void RootWindowViews::SetTitlebarHeight(const std::optional<float>& height) {
if (window_) {
window_->SetTitlebarHeight(height);
}
}
void RootWindowViews::Init(RootWindow::Delegate* delegate,
std::unique_ptr<RootWindowConfig> config,
const CefBrowserSettings& settings) {

View File

@ -26,6 +26,8 @@ class RootWindowViews : public RootWindow,
RootWindowViews();
~RootWindowViews();
void SetTitlebarHeight(const std::optional<float>& height);
// RootWindow methods:
void Init(RootWindow::Delegate* delegate,
std::unique_ptr<RootWindowConfig> config,

View File

@ -399,6 +399,16 @@ bool ViewsWindow::GetWindowRestorePreferences(
return true;
}
void ViewsWindow::SetTitlebarHeight(const std::optional<float>& height) {
CEF_REQUIRE_UI_THREAD();
if (height.has_value()) {
override_titlebar_height_ = height;
} else {
override_titlebar_height_ = default_titlebar_height_;
}
NudgeWindow();
}
CefRefPtr<CefBrowserViewDelegate> ViewsWindow::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
@ -735,8 +745,8 @@ bool ViewsWindow::GetTitlebarHeight(CefRefPtr<CefWindow> window,
float* titlebar_height) {
CEF_REQUIRE_UI_THREAD();
#if defined(OS_MAC)
if (frameless_ && with_standard_buttons_) {
*titlebar_height = kTitleBarHeight;
if (override_titlebar_height_.has_value()) {
*titlebar_height = override_titlebar_height_.value();
return true;
}
#endif
@ -925,6 +935,13 @@ ViewsWindow::ViewsWindow(Delegate* delegate,
// If window has frame or flag passed explicitly
with_standard_buttons_ = !frameless_ || show_window_buttons;
#if defined(OS_MAC)
if (frameless_ && with_standard_buttons_) {
default_titlebar_height_ = kTitleBarHeight;
override_titlebar_height_ = kTitleBarHeight;
}
#endif
const std::string& toolbar_type =
command_line->GetSwitchValue(switches::kShowChromeToolbar);
chrome_toolbar_type_ = CalculateChromeToolbarType(toolbar_type, hide_toolbar,
@ -1262,4 +1279,10 @@ void ViewsWindow::OnExtensionWindowClosed() {
extension_button_pressed_lock_ = nullptr;
}
#if !defined(OS_MAC)
void ViewsWindow::NudgeWindow() {
NOTIMPLEMENTED();
}
#endif
} // namespace client

View File

@ -130,6 +130,7 @@ class ViewsWindow : public CefBrowserViewDelegate,
bool GetWindowRestorePreferences(cef_show_state_t& show_state,
std::optional<CefRect>& dip_bounds);
void SetTitlebarHeight(const std::optional<float>& height);
// CefBrowserViewDelegate methods:
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
@ -236,6 +237,8 @@ class ViewsWindow : public CefBrowserViewDelegate,
const ImageCache::ImageSet& images);
void OnExtensionWindowClosed();
void NudgeWindow();
Delegate* delegate_; // Not owned by this object.
CefRefPtr<CefBrowserView> browser_view_;
bool frameless_;
@ -258,6 +261,9 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefRefPtr<ViewsOverlayControls> overlay_controls_;
std::optional<float> default_titlebar_height_;
std::optional<float> override_titlebar_height_;
// Structure representing an extension.
struct ExtensionInfo {
ExtensionInfo(CefRefPtr<CefExtension> extension, CefRefPtr<CefImage> image)

View File

@ -0,0 +1,24 @@
// Copyright (c) 2023 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include "tests/cefclient/browser/views_window.h"
#include <Cocoa/Cocoa.h>
namespace client {
void ViewsWindow::NudgeWindow() {
if (window_) {
auto view = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(window_->GetWindowHandle());
NSWindow* main_window = view.window;
auto theme_frame = main_window.contentView.superview;
// Nudge view frame a little to force an update.
NSSize size = theme_frame.frame.size;
[theme_frame setFrameSize:NSMakeSize(size.width - 1, size.height)];
[theme_frame setFrameSize:size];
}
}
}

View File

@ -35,6 +35,7 @@ const char kMessagePositionName[] = "WindowTest.Position";
const char kMessageMinimizeName[] = "WindowTest.Minimize";
const char kMessageMaximizeName[] = "WindowTest.Maximize";
const char kMessageRestoreName[] = "WindowTest.Restore";
const char kMessageTitlebarHeightName[] = "WindowTest.TitlebarHeight";
// Create the appropriate platform test runner object.
std::unique_ptr<WindowTestRunner> CreateWindowTestRunner() {
@ -69,6 +70,15 @@ std::vector<int> ParsePosition(const std::string& message_name) {
return vec;
}
std::optional<float> ParseHeight(const std::string& message) {
if (message.size() > sizeof(kMessageTitlebarHeightName)) {
const std::string& val = message.substr(sizeof(kMessageTitlebarHeightName));
return std::stof(val);
} else {
return std::nullopt;
}
}
// Handle messages in the browser process.
class Handler : public CefMessageRouterBrowserSide::Handler {
public:
@ -91,18 +101,17 @@ class Handler : public CefMessageRouterBrowserSide::Handler {
if (message_name.find(kMessagePositionName) == 0) {
const auto vec = ParsePosition(message_name);
if (vec.size() == 4) {
// Execute SetPos() on the main thread.
runner_->SetPos(browser, vec[0], vec[1], vec[2], vec[3]);
}
} else if (message_name == kMessageMinimizeName) {
// Execute Minimize() on the main thread.
runner_->Minimize(browser);
} else if (message_name == kMessageMaximizeName) {
// Execute Maximize() on the main thread.
runner_->Maximize(browser);
} else if (message_name == kMessageRestoreName) {
// Execute Restore() on the main thread.
runner_->Restore(browser);
} else if (message_name.find(kMessageTitlebarHeightName) == 0) {
const auto height = ParseHeight(message_name);
runner_->SetTitleBarHeight(browser, height);
} else {
NOTREACHED();
}

View File

@ -36,5 +36,10 @@ void WindowTestRunner::ModifyBounds(const CefRect& display, CefRect& window) {
}
}
void WindowTestRunner::SetTitleBarHeight(CefRefPtr<CefBrowser> browser,
const std::optional<float>& height) {
NOTIMPLEMENTED();
}
} // namespace window_test
} // namespace client

View File

@ -8,6 +8,8 @@
#include "include/cef_browser.h"
#include <optional>
namespace client {
namespace window_test {
@ -15,6 +17,8 @@ namespace window_test {
// the browser process UI thread unless otherwise indicated.
class WindowTestRunner {
public:
virtual ~WindowTestRunner() = default;
virtual void SetPos(CefRefPtr<CefBrowser> browser,
int x,
int y,
@ -28,7 +32,8 @@ class WindowTestRunner {
// corner of the display.
static void ModifyBounds(const CefRect& display, CefRect& window);
virtual ~WindowTestRunner() {}
virtual void SetTitleBarHeight(CefRefPtr<CefBrowser> browser,
const std::optional<float>& height);
};
} // namespace window_test

View File

@ -9,12 +9,15 @@
#include "include/views/cef_window.h"
#include "include/wrapper/cef_helpers.h"
#include "tests/cefclient/browser/root_window_views.h"
#include "tests/cefclient/browser/views_window.h"
namespace client {
namespace window_test {
namespace {
CefRefPtr<CefWindow> GetWindow(CefRefPtr<CefBrowser> browser) {
CefRefPtr<CefWindow> GetWindow(const CefRefPtr<CefBrowser>& browser) {
CEF_REQUIRE_UI_THREAD();
DCHECK(browser->GetHost()->HasView());
@ -27,6 +30,16 @@ CefRefPtr<CefWindow> GetWindow(CefRefPtr<CefBrowser> browser) {
return window;
}
void SetTitlebarHeight(const CefRefPtr<CefBrowser>& browser,
const std::optional<float>& height) {
CEF_REQUIRE_UI_THREAD();
auto root_window = RootWindow::GetForBrowser(browser->GetIdentifier());
DCHECK(root_window.get());
auto root_window_views = static_cast<RootWindowViews*>(root_window.get());
root_window_views->SetTitlebarHeight(height);
}
} // namespace
WindowTestRunnerViews::WindowTestRunnerViews() {}
@ -56,5 +69,11 @@ void WindowTestRunnerViews::Restore(CefRefPtr<CefBrowser> browser) {
GetWindow(browser)->Restore();
}
void WindowTestRunnerViews::SetTitleBarHeight(
CefRefPtr<CefBrowser> browser,
const std::optional<float>& height) {
SetTitlebarHeight(browser, height);
}
} // namespace window_test
} // namespace client

View File

@ -24,6 +24,8 @@ class WindowTestRunnerViews : public WindowTestRunner {
void Minimize(CefRefPtr<CefBrowser> browser) override;
void Maximize(CefRefPtr<CefBrowser> browser) override;
void Restore(CefRefPtr<CefBrowser> browser) override;
void SetTitleBarHeight(CefRefPtr<CefBrowser> browser,
const std::optional<float>& height) override;
};
} // namespace window_test

View File

@ -1,4 +1,5 @@
<html>
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>Window Test</title>
<script>
@ -47,6 +48,14 @@ function position() {
else
send_message('Position', x + ',' + y + ',' + width + ',' + height);
}
function setTitlebarHeight() {
const height = parseFloat(document.getElementById('title_bar_height').value);
if (isNaN(height))
send_message('TitlebarHeight');
else
send_message('TitlebarHeight', height);
}
</script>
</head>
<body bgcolor="white" onload="setup()">
@ -60,6 +69,8 @@ X: <input type="text" size="4" id="x" value="200">
Y: <input type="text" size="4" id="y" value="100">
Width: <input type="text" size="4" id="width" value="800">
Height: <input type="text" size="4" id="height" value="600">
<br/><input type="button" onclick="setTitlebarHeight();" value="Set Titlebar Height">
<input type="number" min="0" max="100" id="title_bar_height" value="50"> (works on macOS with Views)
</form>
</body>
</html>