views: mac: Fix frameless window behavior (fixes issue #3189)

Frameless windows now display as expected. Default traffic light buttons can
optionally be shown at configurable vertical position. Layout respects text
direction.
This commit is contained in:
Nik Pavlov
2023-02-28 18:34:12 +00:00
committed by Marshall Greenblatt
parent 6926287894
commit f6de0344cb
22 changed files with 579 additions and 57 deletions

View File

@@ -0,0 +1,37 @@
// Copyright 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.
#ifndef CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_
#define CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_
#pragma once
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/views/widget/native_widget_mac.h"
class CefNativeWidgetMac : public views::NativeWidgetMac {
public:
CefNativeWidgetMac(views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height);
~CefNativeWidgetMac() override = default;
CefNativeWidgetMac(const CefNativeWidgetMac&) = delete;
CefNativeWidgetMac& operator=(const CefNativeWidgetMac&) = delete;
protected:
// NativeWidgetMac:
NativeWidgetMacNSWindow* CreateNSWindow(
const remote_cocoa::mojom::CreateWindowParams* params) override;
void GetWindowFrameTitlebarHeight(bool* override_titlebar_height,
float* titlebar_height) override;
private:
const bool is_frameless_;
const bool with_window_buttons_;
const absl::optional<float> title_bar_height_;
};
#endif // CEF_LIBCEF_BROWSER_VIEWS_NATIVE_WIDGET_MAC_H_

View File

@@ -0,0 +1,52 @@
// Copyright 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 "libcef/browser/views/native_widget_mac.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)
: views::NativeWidgetMac(delegate),
is_frameless_(is_frameless),
with_window_buttons_(with_window_buttons),
title_bar_height_(title_bar_height) {}
NativeWidgetMacNSWindow* CefNativeWidgetMac::CreateNSWindow(
const remote_cocoa::mojom::CreateWindowParams* params) {
NSUInteger style_mask =
NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable |
NSWindowStyleMaskClosable | NSWindowStyleMaskResizable |
NSWindowStyleMaskTexturedBackground;
auto window = [[CefNSWindow alloc] initWithStyle:style_mask
isFrameless:is_frameless_];
if (is_frameless_) {
[window setTitlebarAppearsTransparent:YES];
[window setTitleVisibility:NSWindowTitleHidden];
}
if (!with_window_buttons_) {
[[window standardWindowButton:NSWindowCloseButton] setHidden:YES];
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
[[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
}
return window;
}
void CefNativeWidgetMac::GetWindowFrameTitlebarHeight(
bool* override_titlebar_height,
float* titlebar_height) {
if (title_bar_height_) {
*override_titlebar_height = true;
*titlebar_height = title_bar_height_.value();
} else {
views::NativeWidgetMac::GetWindowFrameTitlebarHeight(
override_titlebar_height, titlebar_height);
}
}

View File

@@ -0,0 +1,20 @@
// Copyright 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.
#ifndef CEF_LIBCEF_BROWSER_VIEWS_NS_WINDOW_H_
#define CEF_LIBCEF_BROWSER_VIEWS_NS_WINDOW_H_
#pragma once
#include "components/remote_cocoa/app_shim/native_widget_mac_nswindow.h"
@interface CefNSWindow : NativeWidgetMacNSWindow {
@private
bool is_frameless_;
}
- (id)initWithStyle:(NSUInteger)style_mask isFrameless:(bool)is_frameless;
- (BOOL)shouldCenterTrafficLights;
@end
#endif // CEF_LIBCEF_BROWSER_VIEWS_NS_WINDOW_H_

View File

@@ -0,0 +1,104 @@
// Copyright 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 "libcef/browser/views/ns_window.h"
#include "base/i18n/rtl.h"
#include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
#include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h"
#include "ui/base/cocoa/window_size_constants.h"
@interface CefThemeFrame : NativeWidgetMacNSWindowTitledFrame
@end
// NSThemeFrame (PrivateAPI) definitions.
@interface NSThemeFrame (PrivateAPI)
- (void)setStyleMask:(NSUInteger)styleMask;
- (CGFloat)_titlebarHeight;
- (BOOL)_shouldCenterTrafficLights;
@end
@implementation CefThemeFrame {
bool in_full_screen_;
}
// NSThemeFrame (PrivateAPI) overrides.
- (void)setStyleMask:(NSUInteger)styleMask {
in_full_screen_ = (styleMask & NSWindowStyleMaskFullScreen) != 0;
[super setStyleMask:styleMask];
}
- (CGFloat)_titlebarHeight {
if (!in_full_screen_) {
bool override_titlebar_height = false;
float titlebar_height = 0;
auto* window = base::mac::ObjCCast<CefNSWindow>([self window]);
if (auto* bridge = [window bridge]) {
bridge->host()->GetWindowFrameTitlebarHeight(&override_titlebar_height,
&titlebar_height);
if (override_titlebar_height)
return titlebar_height;
}
}
return [super _titlebarHeight];
}
- (BOOL)_shouldCenterTrafficLights {
auto* window = base::mac::ObjCCast<CefNSWindow>([self window]);
return [window shouldCenterTrafficLights];
}
- (BOOL)_shouldFlipTrafficLightsForRTL {
return base::i18n::IsRTL() ? YES : NO;
}
@end
@interface NSWindow (PrivateAPI)
+ (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle;
@end
@implementation CefNSWindow
- (id)initWithStyle:(NSUInteger)style_mask isFrameless:(bool)is_frameless {
if ((self = [super initWithContentRect:ui::kWindowSizeDeterminedLater
styleMask:style_mask
backing:NSBackingStoreBuffered
defer:NO])) {
is_frameless_ = is_frameless;
}
return self;
}
- (BOOL)shouldCenterTrafficLights {
return is_frameless_ ? YES : NO;
}
// NSWindow overrides.
- (NSRect)contentRectForFrameRect:(NSRect)frameRect {
if (is_frameless_) {
return frameRect;
}
return [super contentRectForFrameRect:frameRect];
}
- (NSRect)frameRectForContentRect:(NSRect)contentRect {
if (is_frameless_) {
return contentRect;
}
return [super frameRectForContentRect:contentRect];
}
// NSWindow (PrivateAPI) overrides.
+ (Class)frameViewClassForStyleMask:(NSUInteger)windowStyle {
if (Class custom_frame = [CefThemeFrame class]) {
return custom_frame;
}
return [super frameViewClassForStyleMask:windowStyle];
}
@end

View File

@@ -9,6 +9,7 @@
#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"
@@ -21,8 +22,12 @@ class Point;
}
namespace views {
class NativeWidget;
class Widget;
namespace internal {
class NativeWidgetDelegate;
}
} // namespace views
#define CEF_REQUIRE_VALID_RETURN(ret) \
if (!ParentClass::IsValid()) \
@@ -141,6 +146,12 @@ CefWindowHandle GetWindowHandle(views::Widget* widget);
// Returns the platform window handle for |window|. May return nullptr.
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);
} // namespace view_util
#endif // CEF_LIBCEF_BROWSER_VIEWS_VIEW_UTIL_H_

View File

@@ -6,6 +6,8 @@
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/views/widget/native_widget.h"
#include "ui/views/widget/native_widget_delegate.h"
#include "ui/views/widget/widget.h"
namespace view_util {
@@ -40,4 +42,12 @@ CefWindowHandle GetWindowHandle(gfx::NativeWindow window) {
return kNullWindowHandle;
}
views::NativeWidget* CreateNativeWidget(
views::internal::NativeWidgetDelegate* delegate,
bool is_frameless,
bool with_window_buttons,
absl::optional<float> title_bar_height) {
return nullptr;
}
} // namespace view_util

View File

@@ -7,6 +7,7 @@
#import <Cocoa/Cocoa.h>
#include "include/internal/cef_types_mac.h"
#include "libcef/browser/views/native_widget_mac.h"
#include "ui/views/widget/widget.h"
@@ -44,4 +45,12 @@ CefWindowHandle GetWindowHandle(gfx::NativeWindow window) {
return kNullWindowHandle;
}
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);
}
} // namespace view_util

View File

@@ -312,6 +312,15 @@ void CefWindowView::CreateWidget(gfx::AcceleratedWidget parent_widget) {
DCHECK(!params.bounds.IsEmpty());
} 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);
can_resize = cef_delegate()->CanResize(cef_window);
const auto show_state = cef_delegate()->GetInitialShowState(cef_window);
@@ -657,3 +666,14 @@ 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,6 +14,7 @@
#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"
@@ -119,6 +120,9 @@ class CefWindowView
void MoveOverlaysIfNecessary();
absl::optional<float> GetTitlebarHeight(
const CefRefPtr<CefWindow>& window) const;
// Not owned by this object.
Delegate* window_delegate_;