Index: ui/views_core/bridged_view_impl_mac.mm |
diff --git a/ui/views_core/bridged_view_impl_mac.mm b/ui/views_core/bridged_view_impl_mac.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f1e73ed558c71e845c81e9729be99892cab9d1a2 |
--- /dev/null |
+++ b/ui/views_core/bridged_view_impl_mac.mm |
@@ -0,0 +1,242 @@ |
+// Copyright 2014 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. |
+ |
+#import "ui/views_core/bridged_view_impl_mac.h" |
+ |
+#include "base/memory/scoped_ptr.h" |
+#include "ui/accessibility/ax_view_state.h" |
+#include "ui/base/layout.h" |
+#include "ui/gfx/canvas.h" |
+#include "ui/gfx/canvas_paint_mac.h" |
+#include "ui/gfx/geometry/vector2d_f.h" |
+#include "ui/gfx/image/image.h" |
+#include "ui/views/view.h" |
+#include "ui/views/widget/root_view.h" |
+#include "ui/views/widget/widget.h" |
+ |
+#include "base/debug/stack_trace.h" |
+ |
+namespace { |
+ |
+template <class OSTREAM> |
+OSTREAM& operator<<(OSTREAM& out, const NSRect& rect) { |
+ CGFloat x = rect.origin.x; |
+ CGFloat y = rect.origin.y; |
+ out << '[' << rect.size.width << 'x' << rect.size.height |
+ << (x < 0 ? '-' : '+') << x << (y < 0 ? '-' : '+') << y << ']'; |
+ return out; |
+} |
+ |
+template <class OSTREAM> |
+OSTREAM& operator<<(OSTREAM& out, const NSPoint& point) { |
+ out << '(' << point.x << ',' << point.y << ')'; |
+ return out; |
+} |
+ |
+} |
+ |
+@interface BridgedViewImpl () |
+ |
+- (void)handleMouseEvent:(NSEvent*)theEvent; |
+ |
+@end |
+ |
+namespace views { |
+ |
+class NSViewNativeWidget : public internal::NativeWidgetPrivate { |
+ |
+}; |
+ |
+class NSViewBridgeWidget : public views::Widget { |
+ public: |
+ NSViewBridgeWidget(BridgedViewImpl* parent) : parent_(parent) {} |
+ |
+ protected: |
+ // views::Widget: |
+ virtual internal::RootView* CreateRootView() OVERRIDE; |
+ |
+ private: |
+ BridgedViewImpl* parent_; // Weak. Owns us. |
+}; |
+ |
+class NSViewBridge : public internal::RootView { |
+ public: |
+ NSViewBridge(views::Widget* widget, BridgedViewImpl* parent) |
+ : internal::RootView(widget), |
+ parent_(parent) { |
+ } |
+ |
+ protected: |
+ // views::View: |
+ virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; |
+ virtual void ViewHierarchyChanged( |
+ const ViewHierarchyChangedDetails& details) OVERRIDE; |
+ virtual void ChildPreferredSizeChanged(View* child) OVERRIDE; |
+ virtual void OnVisibleBoundsChanged() OVERRIDE; |
+ virtual void SchedulePaintInRect(const gfx::Rect& r) OVERRIDE; |
+ |
+ private: |
+ BridgedViewImpl* parent_; // Weak. Owns us. |
+}; |
+ |
+internal::RootView* NSViewBridgeWidget::CreateRootView() { |
+ return new NSViewBridge(this, parent_); |
+} |
+ |
+void NSViewBridge::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
+ [parent_ setFrame:NSRectFromCGRect(bounds().ToCGRect())]; |
+} |
+ |
+void NSViewBridge::ViewHierarchyChanged( |
+ const ViewHierarchyChangedDetails& details) { |
+ if (details.is_add && details.parent == this) { |
+ DCHECK_EQ(1, child_count()); |
+ |
+ // This is kinda backwards, but we need notification of bounds changes. |
+ details.child->AddDescendantToNotify(this); |
+ |
+ DLOG(INFO) << "NSViewBridge hosting: " << details.child->GetClassName(); |
+ } |
+} |
+ |
+void NSViewBridge::ChildPreferredSizeChanged(View* child) { |
+ SetBoundsRect(child->bounds()); |
+} |
+ |
+void NSViewBridge::OnVisibleBoundsChanged() { |
+ ChildPreferredSizeChanged([parent_ view]); |
+} |
+ |
+void NSViewBridge::SchedulePaintInRect(const gfx::Rect& r) { |
+ [parent_ setNeedsDisplay:YES]; |
+} |
+ |
+} // namespace views |
+ |
+@implementation BridgedViewImpl |
+ |
+- (id)initWithView:(scoped_ptr<views::View>)viewToHost { |
+ DCHECK(viewToHost); |
+ CGRect initialFrame = viewToHost->bounds().ToCGRect(); |
+ if ((self = [super initWithFrame:NSRectFromCGRect(initialFrame)])) { |
+ widget_.reset(new views::NSViewBridgeWidget(self)); |
+ views::Widget::InitParams initParams; |
+ initParams.type = views::Widget::InitParams::TYPE_CONTROL; |
+ widget_->Init(initParams); |
+ |
+ widget_->GetRootView()->AddChildView(viewToHost.release()); |
+ hostedView_ = widget_->GetRootView()->child_at(0); |
+ } |
+ return self; |
+} |
+ |
+- (views::View*)view { |
+ return hostedView_; |
+} |
+ |
+- (void)clearView { |
+ hostedView_ = NULL; |
+ bridge_.reset(); |
+} |
+ |
+// BridgedViewImpl private implementation. |
+ |
+- (void)handleMouseEvent:(NSEvent*)theEvent { |
+ if (!hostedView_) |
+ return; |
+ |
+ ui::MouseEvent event(theEvent); |
+ |
+ NSPoint windowLocation = [theEvent locationInWindow]; |
+ NSPoint viewLocation = [self convertPoint:windowLocation fromView:nil]; |
+ event.set_location(event.location() + gfx::Vector2dF( |
+ viewLocation.x - windowLocation.x, |
+ viewLocation.y - windowLocation.y)); |
+ |
+ { |
+ gfx::Point uiViewLocation = event.location(); |
+ views::View::ConvertPointFromWidget(hostedView_, &uiViewLocation); |
+ |
+ DLOG(INFO) << "Forwarding mouse event in " << [self frame] |
+ << " " << hostedView_->GetClassName() << ":" |
+ << " window" << windowLocation |
+ << " view" << viewLocation |
+ << " ui.Location: " << event.location_f().ToString() |
+ << " ui.ViewLocation: " << uiViewLocation.ToString(); |
+ } |
+ |
+ widget_->OnMouseEvent(&event); |
+} |
+ |
+// NSView implementation. |
+ |
+- (void)drawRect:(NSRect)dirtyRect { |
+ if (!hostedView_) |
+ return; |
+ |
+ DLOG(INFO) << "Painting."; |
+ gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */); |
+ hostedView_->Paint(&canvas); |
+} |
+ |
+- (void)mouseDown:(NSEvent*)theEvent { |
+ [self handleMouseEvent:theEvent]; |
+} |
+ |
+- (void)mouseDragged:(NSEvent*)theEvent { |
+ [self handleMouseEvent:theEvent]; |
+} |
+ |
+- (void)mouseUp:(NSEvent*)theEvent { |
+ [self handleMouseEvent:theEvent]; |
+} |
+ |
+// NSAccessibility informal protocol implementation. |
+ |
+- (NSArray*)accessibilityAttributeNames { |
+ NSMutableArray* attributes = |
+ [[super accessibilityAttributeNames] mutableCopy]; |
+ if (!hostedView_) |
+ return [attributes autorelease]; |
+ |
+ [attributes addObject:NSAccessibilityValueAttribute]; |
+ [attributes addObject:NSAccessibilityDescriptionAttribute]; |
+ return [attributes autorelease]; |
+} |
+ |
+- (id)accessibilityAttributeValue:(NSString*)attribute { |
+ if (!hostedView_) |
+ return [super accessibilityAttributeValue:attribute]; |
+ |
+ ui::AXViewState state; |
+ hostedView_->GetAccessibleState(&state); |
+ switch (state.role) { |
+ default: |
+ NOTREACHED(); |
+ } |
+#if 0 |
+ if ([attribute isEqual:NSAccessibilityRoleAttribute]) |
+ return NSAccessibilityTabGroupRole; |
+ if ([attribute isEqual:NSAccessibilityTabsAttribute]) { |
+ NSMutableArray* tabs = [[[NSMutableArray alloc] init] autorelease]; |
+ NSArray* children = |
+ [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; |
+ for (id child in children) { |
+ if ([[child accessibilityAttributeValue:NSAccessibilityRoleAttribute] |
+ isEqual:NSAccessibilityRadioButtonRole]) { |
+ [tabs addObject:child]; |
+ } |
+ } |
+ return tabs; |
+ } |
+ if ([attribute isEqual:NSAccessibilityContentsAttribute]) |
+ return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; |
+ if ([attribute isEqual:NSAccessibilityValueAttribute]) |
+ return [controller_ activeTabView]; |
+#endif |
+ |
+ return [super accessibilityAttributeValue:attribute]; |
+} |
+ |
+@end |