Commit ab8d9781 authored by Christopher Cameron's avatar Christopher Cameron Committed by Commit Bot

RemoteMacViews: Remove most views calls from BridgedContentView

In BridgedContentView, change most calls that refer to the views::View
hostedView_ to be routed through the BridgedNativeWidgetHost interface.

Leave keyboard events, text input, and a11y unchanged for now.

Add a BridgedNativeWidgetHostImpl::SetRootLayer method which now calls
through to BridgedNativeWidget::SetRootLayer. Once the remaining uses
of hostedView_ are removed, the BridgedNativeWidget::SetRootLayer method
will be removed.

Bug: 859152
Change-Id: I2a010b3149357af4f9aeaec0088eb9fcde53db13
Reviewed-on: https://chromium-review.googlesource.com/1168459
Commit-Queue: ccameron <ccameron@chromium.org>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581960}
parent 477d9955
......@@ -16,6 +16,7 @@ class TextInputClient;
}
namespace views {
class BridgedNativeWidgetHost;
class View;
}
......@@ -26,16 +27,22 @@ class View;
NSUserInterfaceValidations,
NSDraggingSource> {
@private
// Weak, reset by clearView.
views::BridgedNativeWidgetHost* host_;
// Weak. The hosted RootView, owned by hostedView_->GetWidget().
// TODO(ccameron): Remove this member.
views::View* hostedView_;
// Weak. If non-null the TextInputClient of the currently focused View in the
// hierarchy rooted at |hostedView_|. Owned by the focused View.
// TODO(ccameron): Remove this member.
ui::TextInputClient* textInputClient_;
// The TextInputClient about to be set. Requests for a new -inputContext will
// use this, but while the input is changing, |self| still needs to service
// IME requests using the old |textInputClient_|.
// TODO(ccameron): Remove this member.
ui::TextInputClient* pendingTextInputClient_;
// A tracking area installed to enable mouseMoved events.
......@@ -56,7 +63,8 @@ class View;
@property(assign, nonatomic) BOOL drawMenuBackgroundForBlur;
// Initialize the NSView -> views::View bridge. |viewToHost| must be non-NULL.
- (id)initWithView:(views::View*)viewToHost;
- (id)initWithHost:(views::BridgedNativeWidgetHost*)host
view:(views::View*)viewToHost;
// Clear the hosted view. For example, if it is about to be destroyed.
- (void)clearView;
......
......@@ -14,7 +14,6 @@
#include "ui/base/cocoa/cocoa_base_utils.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/dragdrop/os_exchange_data_provider_mac.h"
#include "ui/base/hit_test.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_edit_commands.h"
#include "ui/base/ime/text_input_client.h"
......@@ -32,6 +31,7 @@
#import "ui/gfx/path_mac.h"
#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
#import "ui/views/cocoa/bridged_native_widget.h"
#import "ui/views/cocoa/bridged_native_widget_host.h"
#import "ui/views/cocoa/drag_drop_client_mac.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/menu/menu_config.h"
......@@ -39,7 +39,6 @@
#include "ui/views/view.h"
#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/widget/widget.h"
#include "ui/views/word_lookup_client.h"
using views::MenuController;
......@@ -277,7 +276,8 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
@synthesize textInputClient = textInputClient_;
@synthesize drawMenuBackgroundForBlur = drawMenuBackgroundForBlur_;
- (id)initWithView:(views::View*)viewToHost {
- (id)initWithHost:(views::BridgedNativeWidgetHost*)host
view:(views::View*)viewToHost {
DCHECK(viewToHost);
gfx::Rect bounds = viewToHost->bounds();
// To keep things simple, assume the origin is (0, 0) until there exists a use
......@@ -285,6 +285,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
DCHECK(bounds.origin().IsOrigin());
NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
if ((self = [super initWithFrame:initialFrame])) {
host_ = host;
hostedView_ = viewToHost;
// Apple's documentation says that NSTrackingActiveAlways is incompatible
......@@ -326,6 +327,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
- (void)clearView {
[self setTextInputClient:nullptr];
host_ = nullptr;
hostedView_ = nullptr;
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
[cursorTrackingArea_.get() clearOwner];
......@@ -391,20 +393,21 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
}
}
// If |point| is classified as HTCAPTION (draggable background), return nil so
// If |point| is classified as a draggable background (HTCAPTION), return nil so
// that it can lead to a window drag or double-click in the title bar. Dragging
// could be optimized by telling the window server which regions should be
// instantly draggable without asking (tracked at https://crbug.com/830962).
- (NSView*)hitTest:(NSPoint)point {
gfx::Point flippedPoint(point.x, NSHeight(self.superview.bounds) - point.y);
int component = hostedView_->GetWidget()->GetNonClientComponent(flippedPoint);
if (component == HTCAPTION)
bool isDraggableBackground = false;
host_->GetIsDraggableBackgroundAt(flippedPoint, &isDraggableBackground);
if (isDraggableBackground)
return nil;
return [super hitTest:point];
}
- (void)processCapturedMouseEvent:(NSEvent*)theEvent {
if (!hostedView_)
if (!host_)
return;
NSWindow* source = [theEvent window];
......@@ -430,26 +433,19 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
if (isScrollEvent) {
ui::ScrollEvent event(theEvent);
event.set_location(event_location);
hostedView_->GetWidget()->OnScrollEvent(&event);
host_->OnScrollEvent(event);
} else {
ui::MouseEvent event(theEvent);
event.set_location(event_location);
hostedView_->GetWidget()->OnMouseEvent(&event);
host_->OnMouseEvent(event);
}
}
- (void)updateTooltipIfRequiredAt:(const gfx::Point&)locationInContent {
DCHECK(hostedView_);
DCHECK(host_);
base::string16 newTooltipText;
views::View* view = hostedView_->GetTooltipHandlerForPoint(locationInContent);
if (view) {
gfx::Point viewPoint = locationInContent;
views::View::ConvertPointToScreen(hostedView_, &viewPoint);
views::View::ConvertPointFromScreen(view, &viewPoint);
if (!view->GetTooltipText(viewPoint, &newTooltipText))
DCHECK(newTooltipText.empty());
}
host_->GetTooltipTextAt(locationInContent, &newTooltipText);
if (newTooltipText != lastTooltipText_) {
std::swap(newTooltipText, lastTooltipText_);
[self setToolTipAtMousePoint:base::SysUTF16ToNSString(lastTooltipText_)];
......@@ -457,13 +453,9 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
}
- (void)updateFullKeyboardAccess {
if (!hostedView_)
if (!host_)
return;
views::FocusManager* focusManager =
hostedView_->GetWidget()->GetFocusManager();
if (focusManager)
focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]);
host_->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]);
}
// BridgedContentView private implementation.
......@@ -689,7 +681,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// Translates the location of |theEvent| to toolkit-views coordinates and passes
// the event to NativeWidgetMac for handling.
- (void)mouseEvent:(NSEvent*)theEvent {
if (!hostedView_)
if (!host_)
return;
DCHECK([theEvent type] != NSScrollWheel);
......@@ -699,8 +691,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// Aura updates tooltips with the help of aura::Window::AddPreTargetHandler().
// Mac hooks in here.
[self updateTooltipIfRequiredAt:event.location()];
hostedView_->GetWidget()->OnMouseEvent(&event);
host_->OnMouseEvent(event);
}
- (void)forceTouchEvent:(NSEvent*)theEvent {
......@@ -720,15 +711,15 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
if ([[self window] firstResponder] != self)
return NO;
BOOL result = [super becomeFirstResponder];
if (result && hostedView_)
hostedView_->GetWidget()->GetFocusManager()->RestoreFocusedView();
if (result && host_)
host_->SetIsFirstResponder(true);
return result;
}
- (BOOL)resignFirstResponder {
BOOL result = [super resignFirstResponder];
if (result && hostedView_)
hostedView_->GetWidget()->GetFocusManager()->StoreFocusedView(true);
if (result && host_)
host_->SetIsFirstResponder(false);
return result;
}
......@@ -751,16 +742,16 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
newSize = [window contentRectForFrameRect:[window frame]].size;
[super setFrameSize:newSize];
if (!hostedView_)
if (!host_)
return;
hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
host_->SetSize(gfx::Size(newSize.width, newSize.height));
}
- (BOOL)isOpaque {
if (!hostedView_)
return NO;
// TODO(ccameron): Plumb this from BridgedNativeWidget to here.
ui::Layer* layer = hostedView_->GetWidget()->GetLayer();
return layer && layer->fills_bounds_opaquely();
}
......@@ -857,7 +848,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
}
- (void)scrollWheel:(NSEvent*)theEvent {
if (!hostedView_)
if (!host_)
return;
ui::ScrollEvent event(theEvent);
......@@ -866,14 +857,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
// Aura updates tooltips with the help of aura::Window::AddPreTargetHandler().
// Mac hooks in here.
[self updateTooltipIfRequiredAt:event.location()];
hostedView_->GetWidget()->OnScrollEvent(&event);
host_->OnScrollEvent(event);
}
// Called when we get a three-finger swipe, and they're enabled in System
// Preferences.
- (void)swipeWithEvent:(NSEvent*)event {
if (!hostedView_)
if (!host_)
return;
// themblsha: In my testing all three-finger swipes send only a single event
......@@ -894,35 +884,24 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) {
ui::GestureEvent gestureEvent(location.x(), location.y(),
ui::EventFlagsFromNative(event),
ui::EventTimeFromNative(event), swipeDetails);
hostedView_->GetWidget()->OnGestureEvent(&gestureEvent);
host_->OnGestureEvent(gestureEvent);
}
- (void)quickLookWithEvent:(NSEvent*)theEvent {
if (!hostedView_)
if (!host_)
return;
const gfx::Point locationInContent =
gfx::ToFlooredPoint(ui::EventLocationFromNative(theEvent));
views::View* target = hostedView_->GetEventHandlerForPoint(locationInContent);
if (!target)
return;
views::WordLookupClient* wordLookupClient = target->GetWordLookupClient();
if (!wordLookupClient)
return;
gfx::Point locationInTarget = locationInContent;
views::View::ConvertPointToTarget(hostedView_, target, &locationInTarget);
bool foundWord = false;
gfx::DecoratedText decoratedWord;
gfx::Point baselinePoint;
if (!wordLookupClient->GetWordLookupDataAtPoint(
locationInTarget, &decoratedWord, &baselinePoint)) {
host_->GetWordAt(locationInContent, &foundWord, &decoratedWord,
&baselinePoint);
if (!foundWord)
return;
}
// Convert |baselinePoint| to the coordinate system of |hostedView_|.
views::View::ConvertPointToTarget(target, hostedView_, &baselinePoint);
NSPoint baselinePointAppKit = NSMakePoint(
baselinePoint.x(), NSHeight([self frame]) - baselinePoint.y());
[self showDefinitionForAttributedString:
......
......@@ -444,7 +444,8 @@ void BridgedNativeWidget::SetRootView(views::View* view) {
// the old views::View will be gone, so any method calls will become no-ops.
if (view) {
bridged_view_.reset([[BridgedContentView alloc] initWithView:view]);
bridged_view_.reset(
[[BridgedContentView alloc] initWithHost:host_ view:view]);
drag_drop_client_.reset(new DragDropClientMac(this, view));
// Objective C initializers can return nil. However, if |view| is non-NULL
......
......@@ -5,6 +5,10 @@
#ifndef UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_H_
#define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_H_
#include "ui/events/event_utils.h"
#include "ui/gfx/decorated_text.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/views_export.h"
namespace views {
......@@ -21,6 +25,37 @@ class VIEWS_EXPORT BridgedNativeWidgetHost {
// Update the ui::Compositor and ui::Layer's visibility.
virtual void SetCompositorVisibility(bool visible) = 0;
// Resize the underlying views::View to |new_size|.
virtual void SetSize(const gfx::Size& new_size) = 0;
// Indicate if full keyboard accessibility is needed and updates focus if
// needed.
virtual void SetKeyboardAccessible(bool enabled) = 0;
// Indicate if the NSView is the first responder.
virtual void SetIsFirstResponder(bool is_first_responder) = 0;
// Handle events. Note that whether or not the event is actually handled is
// not returned.
virtual void OnScrollEvent(const ui::ScrollEvent& const_event) = 0;
virtual void OnMouseEvent(const ui::MouseEvent& const_event) = 0;
virtual void OnGestureEvent(const ui::GestureEvent& const_event) = 0;
// Synchronously query if |location_in_content| is a draggable background.
virtual void GetIsDraggableBackgroundAt(const gfx::Point& location_in_content,
bool* is_draggable_background) = 0;
// Synchronously query the tooltip text for |location_in_content|.
virtual void GetTooltipTextAt(const gfx::Point& location_in_content,
base::string16* new_tooltip_text) = 0;
// Synchronously query the quicklook text at |location_in_content|. Return in
// |found_word| whether or not a word was found.
virtual void GetWordAt(const gfx::Point& location_in_content,
bool* found_word,
gfx::DecoratedText* decorated_word,
gfx::Point* baseline_point) = 0;
};
} // namespace views
......
......@@ -41,27 +41,46 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl
// with methods that may be sent across processes.
BridgedNativeWidget* bridge() const { return bridge_.get(); }
// Set the root view (set during initialization and un-set during teardown).
void SetRootView(views::View* root_view);
// Initialize the ui::Compositor and ui::Layer.
void CreateCompositor(const Widget::InitParams& params);
private:
void DestroyCompositor();
// Overridden from views::BridgedNativeWidgetHost:
// views::BridgedNativeWidgetHost:
void SetCompositorSize(const gfx::Size& size_in_dip,
float scale_factor) override;
void SetCompositorVisibility(bool visible) override;
// Overridden from ui::LayerDelegate:
void SetSize(const gfx::Size& new_size) override;
void SetKeyboardAccessible(bool enabled) override;
void SetIsFirstResponder(bool is_first_responder) override;
void OnScrollEvent(const ui::ScrollEvent& const_event) override;
void OnMouseEvent(const ui::MouseEvent& const_event) override;
void OnGestureEvent(const ui::GestureEvent& const_event) override;
void GetIsDraggableBackgroundAt(const gfx::Point& location_in_content,
bool* is_draggable_background) override;
void GetTooltipTextAt(const gfx::Point& location_in_content,
base::string16* new_tooltip_text) override;
void GetWordAt(const gfx::Point& location_in_content,
bool* found_word,
gfx::DecoratedText* decorated_word,
gfx::Point* baseline_point) override;
// ui::LayerDelegate:
void OnPaintLayer(const ui::PaintContext& context) override;
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override;
// Overridden from ui::AcceleratedWidgetMacNSView:
// ui::AcceleratedWidgetMacNSView:
void AcceleratedWidgetCALayerParamsUpdated() override;
views::NativeWidgetMac* const native_widget_mac_; // Weak. Owns |this_|.
views::View* root_view_ = nullptr; // Weak. Owned by |native_widget_mac_|.
// TODO(ccameron): Rather than instantiate a BridgedNativeWidget here,
// we will instantiate a mojo BridgedNativeWidget interface to a Cocoa
// instance that may be in another process.
......
......@@ -4,11 +4,13 @@
#include "ui/views/cocoa/bridged_native_widget_host_impl.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/recyclable_compositor_mac.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/views/cocoa/bridged_native_widget.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/native_widget_mac.h"
#include "ui/views/word_lookup_client.h"
namespace views {
......@@ -26,6 +28,13 @@ BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() {
DestroyCompositor();
}
void BridgedNativeWidgetHostImpl::SetRootView(views::View* root_view) {
root_view_ = root_view;
// TODO(ccameron): The BridgedNativeWidget should not need to know its root
// view.
bridge_->SetRootView(root_view);
}
void BridgedNativeWidgetHostImpl::CreateCompositor(
const Widget::InitParams& params) {
DCHECK(!compositor_);
......@@ -100,6 +109,92 @@ void BridgedNativeWidgetHostImpl::SetCompositorVisibility(bool window_visible) {
}
}
void BridgedNativeWidgetHostImpl::OnScrollEvent(
const ui::ScrollEvent& const_event) {
ui::ScrollEvent event = const_event;
root_view_->GetWidget()->OnScrollEvent(&event);
}
void BridgedNativeWidgetHostImpl::OnMouseEvent(
const ui::MouseEvent& const_event) {
ui::MouseEvent event = const_event;
root_view_->GetWidget()->OnMouseEvent(&event);
}
void BridgedNativeWidgetHostImpl::OnGestureEvent(
const ui::GestureEvent& const_event) {
ui::GestureEvent event = const_event;
root_view_->GetWidget()->OnGestureEvent(&event);
}
void BridgedNativeWidgetHostImpl::SetSize(const gfx::Size& new_size) {
root_view_->SetSize(new_size);
}
void BridgedNativeWidgetHostImpl::SetKeyboardAccessible(bool enabled) {
views::FocusManager* focus_manager =
root_view_->GetWidget()->GetFocusManager();
if (focus_manager)
focus_manager->SetKeyboardAccessible(enabled);
}
void BridgedNativeWidgetHostImpl::SetIsFirstResponder(bool is_first_responder) {
if (is_first_responder)
root_view_->GetWidget()->GetFocusManager()->RestoreFocusedView();
else
root_view_->GetWidget()->GetFocusManager()->StoreFocusedView(true);
}
void BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt(
const gfx::Point& location_in_content,
bool* is_draggable_background) {
int component =
root_view_->GetWidget()->GetNonClientComponent(location_in_content);
*is_draggable_background = component == HTCAPTION;
}
void BridgedNativeWidgetHostImpl::GetTooltipTextAt(
const gfx::Point& location_in_content,
base::string16* new_tooltip_text) {
views::View* view =
root_view_->GetTooltipHandlerForPoint(location_in_content);
if (view) {
gfx::Point view_point = location_in_content;
views::View::ConvertPointToScreen(root_view_, &view_point);
views::View::ConvertPointFromScreen(view, &view_point);
if (!view->GetTooltipText(view_point, new_tooltip_text))
DCHECK(new_tooltip_text->empty());
}
}
void BridgedNativeWidgetHostImpl::GetWordAt(
const gfx::Point& location_in_content,
bool* found_word,
gfx::DecoratedText* decorated_word,
gfx::Point* baseline_point) {
*found_word = false;
views::View* target =
root_view_->GetEventHandlerForPoint(location_in_content);
if (!target)
return;
views::WordLookupClient* word_lookup_client = target->GetWordLookupClient();
if (!word_lookup_client)
return;
gfx::Point location_in_target = location_in_content;
views::View::ConvertPointToTarget(root_view_, target, &location_in_target);
if (!word_lookup_client->GetWordLookupDataAtPoint(
location_in_target, decorated_word, baseline_point)) {
return;
}
// Convert |baselinePoint| to the coordinate system of |root_view_|.
views::View::ConvertPointToTarget(target, root_view_, baseline_point);
*found_word = true;
}
////////////////////////////////////////////////////////////////////////////////
// BridgedNativeWidgetHostImpl, LayerDelegate:
......
......@@ -26,6 +26,7 @@
#include "ui/events/test/cocoa_test_event_utils.h"
#import "ui/gfx/mac/coordinate_conversion.h"
#import "ui/views/cocoa/bridged_content_view.h"
#import "ui/views/cocoa/bridged_native_widget_host_impl.h"
#import "ui/views/cocoa/native_widget_mac_nswindow.h"
#import "ui/views/cocoa/views_nswindow_delegate.h"
#include "ui/views/controls/textfield/textfield.h"
......@@ -289,6 +290,7 @@ class MockNativeWidgetMac : public NativeWidgetMac {
explicit MockNativeWidgetMac(internal::NativeWidgetDelegate* delegate)
: NativeWidgetMac(delegate) {}
using NativeWidgetMac::bridge;
using NativeWidgetMac::bridge_host_for_testing;
// internal::NativeWidgetPrivate:
void InitNativeWidget(const Widget::InitParams& params) override {
......@@ -323,6 +325,9 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest {
: native_widget_mac_(nullptr) {}
BridgedNativeWidget* bridge() { return native_widget_mac_->bridge(); }
BridgedNativeWidgetHostImpl* bridge_host() {
return native_widget_mac_->bridge_host_for_testing();
}
// Overridden from testing::Test:
void SetUp() override {
......@@ -571,7 +576,7 @@ void BridgedNativeWidgetTest::SetUp() {
// The delegate should exist before setting the root view.
EXPECT_TRUE([window delegate]);
bridge()->SetRootView(view_.get());
bridge_host()->SetRootView(view_.get());
ns_view_ = bridge()->ns_view();
// Pretend it has been shown via NativeWidgetMac::Show().
......@@ -583,8 +588,8 @@ void BridgedNativeWidgetTest::TearDown() {
// Clear kill buffer so that no state persists between tests.
TextfieldModel::ClearKillBuffer();
if (bridge())
bridge()->SetRootView(nullptr);
if (bridge_host())
bridge_host()->SetRootView(nullptr);
view_.reset();
BridgedNativeWidgetTestBase::TearDown();
}
......
......@@ -155,6 +155,9 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate {
internal::NativeWidgetDelegate* delegate() { return delegate_; }
BridgedNativeWidget* bridge() const;
BridgedNativeWidgetHostImpl* bridge_host_for_testing() const {
return bridge_host_.get();
}
private:
friend class test::MockNativeWidgetMac;
......
......@@ -141,7 +141,7 @@ void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) {
delegate_->OnNativeWidgetCreated(true);
DCHECK(GetWidget()->GetRootView());
bridge()->SetRootView(GetWidget()->GetRootView());
bridge_host_->SetRootView(GetWidget()->GetRootView());
if (auto* focus_manager = GetWidget()->GetFocusManager()) {
[window makeFirstResponder:bridge()->ns_view()];
bridge()->SetFocusManager(focus_manager);
......@@ -399,7 +399,7 @@ void NativeWidgetMac::Close() {
}
// Clear the view early to suppress repaints.
bridge()->SetRootView(nullptr);
bridge_host_->SetRootView(nullptr);
// Widget::Close() ensures [Non]ClientView::CanClose() returns true, so there
// is no need to call the NSWindow or its delegate's -windowShouldClose:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment