Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3214)

Unified Diff: chrome/browser/ui/cocoa/bubble_anchor_helper_views.mm

Issue 2586373003: MacViews: Allow toolkit-views for "Global Error" bubbles. (Closed)
Patch Set: unstage Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/bubble_anchor_helper_views.mm
diff --git a/chrome/browser/ui/cocoa/bubble_anchor_helper_views.mm b/chrome/browser/ui/cocoa/bubble_anchor_helper_views.mm
new file mode 100644
index 0000000000000000000000000000000000000000..e3db9714bf77d1840c9be60c6dda32b312c1acfc
--- /dev/null
+++ b/chrome/browser/ui/cocoa/bubble_anchor_helper_views.mm
@@ -0,0 +1,148 @@
+// Copyright 2017 The Chromium 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 "chrome/browser/ui/cocoa/bubble_anchor_helper_views.h"
+
+#import <Cocoa/Cocoa.h>
+
+#import "base/mac/scoped_nsobject.h"
+#include "ui/views/bubble/bubble_dialog_delegate.h"
+#include "ui/views/widget/widget_observer.h"
+
+@class BubbleAnchorHelperBridge;
+
+namespace {
+
+// Self-deleting object that hosts an Objective-C observer class watching for
+// parent window resizes to reposition a bubble Widget. Deletes itself when the
+// bubble Widget closes.
+class BubbleAnchorHelper : public views::WidgetObserver {
+ public:
+ explicit BubbleAnchorHelper(views::BubbleDialogDelegateView* bubble);
+ ~BubbleAnchorHelper() override;
+
+ // Re-positions |bubble_| so that the offset to the parent window at
+ // construction time is preserved.
+ void ReAnchor();
+
+ // WidgetObserver:
+ void OnWidgetDestroying(views::Widget* widget) override;
+
+ private:
+ // Whether offset from the left of the parent window is fixed.
+ bool IsMinXFixed() const {
+ return views::BubbleBorder::is_arrow_on_left(bubble_->arrow());
+ }
+
+ base::scoped_nsobject<BubbleAnchorHelperBridge> bridge_;
+ views::BubbleDialogDelegateView* bubble_;
+ CGFloat horizontal_offset_; // Offset from the left or right.
+ CGFloat vertical_offset_; // Offset from the top.
+
+ DISALLOW_COPY_AND_ASSIGN(BubbleAnchorHelper);
+};
+
+} // namespace
+
+void KeepBubbleAnchored(views::BubbleDialogDelegateView* bubble) {
+ new BubbleAnchorHelper(bubble);
+}
+
+// Observes changes to the size of a parent NSWindow and asks |helper| to re-
+// anchor the bubble.
+@interface BubbleAnchorHelperBridge : NSObject
+- (instancetype)initWithAnchorHelper:(BubbleAnchorHelper*)helper
+ parentWindow:(NSWindow*)parentWindow;
+- (void)clearOwner;
+@end
+
+BubbleAnchorHelper::BubbleAnchorHelper(views::BubbleDialogDelegateView* bubble)
+ : bubble_(bubble) {
+ DCHECK(bubble->GetWidget());
+ DCHECK(bubble->parent_window());
+ bubble->GetWidget()->AddObserver(this);
+
+ NSRect parent_frame = [[bubble->parent_window() window] frame];
+ NSRect bubble_frame = [bubble->GetWidget()->GetNativeWindow() frame];
+
+ // Note: when anchored on the right, this doesn't support changes to the
+ // bubble size, just the parent size.
+ horizontal_offset_ =
+ (IsMinXFixed() ? NSMinX(parent_frame) : NSMaxX(parent_frame)) -
+ NSMinX(bubble_frame);
+ vertical_offset_ = NSMaxY(parent_frame) - NSMinY(bubble_frame);
+
+ bridge_.reset([[BubbleAnchorHelperBridge alloc]
+ initWithAnchorHelper:this
+ parentWindow:[bubble->parent_window() window]]);
+}
+
+BubbleAnchorHelper::~BubbleAnchorHelper() {
+ [bridge_ clearOwner];
+}
+
+void BubbleAnchorHelper::ReAnchor() {
+ NSRect bubble_frame = [bubble_->GetWidget()->GetNativeWindow() frame];
+ NSRect parent_frame = [[bubble_->parent_window() window] frame];
+ if (IsMinXFixed())
+ bubble_frame.origin.x = NSMinX(parent_frame) - horizontal_offset_;
+ else
+ bubble_frame.origin.x = NSMaxX(parent_frame) - horizontal_offset_;
+ bubble_frame.origin.y = NSMaxY(parent_frame) - vertical_offset_;
+ [bubble_->GetWidget()->GetNativeWindow() setFrame:bubble_frame
+ display:YES
+ animate:NO];
+}
+
+void BubbleAnchorHelper::OnWidgetDestroying(views::Widget* widget) {
+ widget->RemoveObserver(this);
+ delete this;
+}
+
+@implementation BubbleAnchorHelperBridge {
+ BubbleAnchorHelper* owner_;
+}
+
+- (instancetype)initWithAnchorHelper:(BubbleAnchorHelper*)helper
Robert Sesek 2017/01/05 19:50:08 If you use the block-based -[NSNotificationCenter
tapted 2017/01/06 00:14:04 Oooh neato. Done.
+ parentWindow:(NSWindow*)parentWindow {
+ if ((self = [super init])) {
+ owner_ = helper;
+ NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+ [center addObserver:self
+ selector:@selector(reAnchor:)
+ name:NSWindowDidEnterFullScreenNotification
+ object:parentWindow];
+ [center addObserver:self
+ selector:@selector(reAnchor:)
+ name:NSWindowDidExitFullScreenNotification
+ object:parentWindow];
+ [center addObserver:self
+ selector:@selector(reAnchor:)
+ name:NSWindowDidResizeNotification
+ object:parentWindow];
+
+ // Also monitor move. Note that for user-initiated window moves this is not
+ // necessary: the bubble's child window status keeps the position pinned to
+ // the parent during the move (which is handy since AppKit doesn't send out
+ // notifications until the move completes). Programmatic -[NSWindow
+ // setFrame:..] calls, however, do not update child window positions for us.
+ [center addObserver:self
+ selector:@selector(reAnchor:)
+ name:NSWindowDidMoveNotification
+ object:parentWindow];
+ }
+ return self;
+}
+
+- (void)clearOwner {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ owner_ = nullptr;
+}
+
+- (void)reAnchor:(NSNotification*)notification {
+ if (owner_)
+ owner_->ReAnchor();
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698