OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ui/views_core/bridged_view_impl_mac.h" |
| 6 |
| 7 #include "base/memory/scoped_ptr.h" |
| 8 #include "ui/accessibility/ax_view_state.h" |
| 9 #include "ui/base/layout.h" |
| 10 #include "ui/gfx/canvas.h" |
| 11 #include "ui/gfx/canvas_paint_mac.h" |
| 12 #include "ui/gfx/geometry/vector2d_f.h" |
| 13 #include "ui/gfx/image/image.h" |
| 14 #include "ui/views/view.h" |
| 15 #include "ui/views/widget/root_view.h" |
| 16 #include "ui/views/widget/widget.h" |
| 17 |
| 18 #include "base/debug/stack_trace.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 template <class OSTREAM> |
| 23 OSTREAM& operator<<(OSTREAM& out, const NSRect& rect) { |
| 24 CGFloat x = rect.origin.x; |
| 25 CGFloat y = rect.origin.y; |
| 26 out << '[' << rect.size.width << 'x' << rect.size.height |
| 27 << (x < 0 ? '-' : '+') << x << (y < 0 ? '-' : '+') << y << ']'; |
| 28 return out; |
| 29 } |
| 30 |
| 31 template <class OSTREAM> |
| 32 OSTREAM& operator<<(OSTREAM& out, const NSPoint& point) { |
| 33 out << '(' << point.x << ',' << point.y << ')'; |
| 34 return out; |
| 35 } |
| 36 |
| 37 } |
| 38 |
| 39 @interface BridgedViewImpl () |
| 40 |
| 41 - (void)handleMouseEvent:(NSEvent*)theEvent; |
| 42 |
| 43 @end |
| 44 |
| 45 namespace views { |
| 46 |
| 47 class NSViewNativeWidget : public internal::NativeWidgetPrivate { |
| 48 |
| 49 }; |
| 50 |
| 51 class NSViewBridgeWidget : public views::Widget { |
| 52 public: |
| 53 NSViewBridgeWidget(BridgedViewImpl* parent) : parent_(parent) {} |
| 54 |
| 55 protected: |
| 56 // views::Widget: |
| 57 virtual internal::RootView* CreateRootView() OVERRIDE; |
| 58 |
| 59 private: |
| 60 BridgedViewImpl* parent_; // Weak. Owns us. |
| 61 }; |
| 62 |
| 63 class NSViewBridge : public internal::RootView { |
| 64 public: |
| 65 NSViewBridge(views::Widget* widget, BridgedViewImpl* parent) |
| 66 : internal::RootView(widget), |
| 67 parent_(parent) { |
| 68 } |
| 69 |
| 70 protected: |
| 71 // views::View: |
| 72 virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) OVERRIDE; |
| 73 virtual void ViewHierarchyChanged( |
| 74 const ViewHierarchyChangedDetails& details) OVERRIDE; |
| 75 virtual void ChildPreferredSizeChanged(View* child) OVERRIDE; |
| 76 virtual void OnVisibleBoundsChanged() OVERRIDE; |
| 77 virtual void SchedulePaintInRect(const gfx::Rect& r) OVERRIDE; |
| 78 |
| 79 private: |
| 80 BridgedViewImpl* parent_; // Weak. Owns us. |
| 81 }; |
| 82 |
| 83 internal::RootView* NSViewBridgeWidget::CreateRootView() { |
| 84 return new NSViewBridge(this, parent_); |
| 85 } |
| 86 |
| 87 void NSViewBridge::OnBoundsChanged(const gfx::Rect& previous_bounds) { |
| 88 [parent_ setFrame:NSRectFromCGRect(bounds().ToCGRect())]; |
| 89 } |
| 90 |
| 91 void NSViewBridge::ViewHierarchyChanged( |
| 92 const ViewHierarchyChangedDetails& details) { |
| 93 if (details.is_add && details.parent == this) { |
| 94 DCHECK_EQ(1, child_count()); |
| 95 |
| 96 // This is kinda backwards, but we need notification of bounds changes. |
| 97 details.child->AddDescendantToNotify(this); |
| 98 |
| 99 DLOG(INFO) << "NSViewBridge hosting: " << details.child->GetClassName(); |
| 100 } |
| 101 } |
| 102 |
| 103 void NSViewBridge::ChildPreferredSizeChanged(View* child) { |
| 104 SetBoundsRect(child->bounds()); |
| 105 } |
| 106 |
| 107 void NSViewBridge::OnVisibleBoundsChanged() { |
| 108 ChildPreferredSizeChanged([parent_ view]); |
| 109 } |
| 110 |
| 111 void NSViewBridge::SchedulePaintInRect(const gfx::Rect& r) { |
| 112 [parent_ setNeedsDisplay:YES]; |
| 113 } |
| 114 |
| 115 } // namespace views |
| 116 |
| 117 @implementation BridgedViewImpl |
| 118 |
| 119 - (id)initWithView:(scoped_ptr<views::View>)viewToHost { |
| 120 DCHECK(viewToHost); |
| 121 CGRect initialFrame = viewToHost->bounds().ToCGRect(); |
| 122 if ((self = [super initWithFrame:NSRectFromCGRect(initialFrame)])) { |
| 123 widget_.reset(new views::NSViewBridgeWidget(self)); |
| 124 views::Widget::InitParams initParams; |
| 125 initParams.type = views::Widget::InitParams::TYPE_CONTROL; |
| 126 widget_->Init(initParams); |
| 127 |
| 128 widget_->GetRootView()->AddChildView(viewToHost.release()); |
| 129 hostedView_ = widget_->GetRootView()->child_at(0); |
| 130 } |
| 131 return self; |
| 132 } |
| 133 |
| 134 - (views::View*)view { |
| 135 return hostedView_; |
| 136 } |
| 137 |
| 138 - (void)clearView { |
| 139 hostedView_ = NULL; |
| 140 bridge_.reset(); |
| 141 } |
| 142 |
| 143 // BridgedViewImpl private implementation. |
| 144 |
| 145 - (void)handleMouseEvent:(NSEvent*)theEvent { |
| 146 if (!hostedView_) |
| 147 return; |
| 148 |
| 149 ui::MouseEvent event(theEvent); |
| 150 |
| 151 NSPoint windowLocation = [theEvent locationInWindow]; |
| 152 NSPoint viewLocation = [self convertPoint:windowLocation fromView:nil]; |
| 153 event.set_location(event.location() + gfx::Vector2dF( |
| 154 viewLocation.x - windowLocation.x, |
| 155 viewLocation.y - windowLocation.y)); |
| 156 |
| 157 { |
| 158 gfx::Point uiViewLocation = event.location(); |
| 159 views::View::ConvertPointFromWidget(hostedView_, &uiViewLocation); |
| 160 |
| 161 DLOG(INFO) << "Forwarding mouse event in " << [self frame] |
| 162 << " " << hostedView_->GetClassName() << ":" |
| 163 << " window" << windowLocation |
| 164 << " view" << viewLocation |
| 165 << " ui.Location: " << event.location_f().ToString() |
| 166 << " ui.ViewLocation: " << uiViewLocation.ToString(); |
| 167 } |
| 168 |
| 169 widget_->OnMouseEvent(&event); |
| 170 } |
| 171 |
| 172 // NSView implementation. |
| 173 |
| 174 - (void)drawRect:(NSRect)dirtyRect { |
| 175 if (!hostedView_) |
| 176 return; |
| 177 |
| 178 DLOG(INFO) << "Painting."; |
| 179 gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */); |
| 180 hostedView_->Paint(&canvas); |
| 181 } |
| 182 |
| 183 - (void)mouseDown:(NSEvent*)theEvent { |
| 184 [self handleMouseEvent:theEvent]; |
| 185 } |
| 186 |
| 187 - (void)mouseDragged:(NSEvent*)theEvent { |
| 188 [self handleMouseEvent:theEvent]; |
| 189 } |
| 190 |
| 191 - (void)mouseUp:(NSEvent*)theEvent { |
| 192 [self handleMouseEvent:theEvent]; |
| 193 } |
| 194 |
| 195 // NSAccessibility informal protocol implementation. |
| 196 |
| 197 - (NSArray*)accessibilityAttributeNames { |
| 198 NSMutableArray* attributes = |
| 199 [[super accessibilityAttributeNames] mutableCopy]; |
| 200 if (!hostedView_) |
| 201 return [attributes autorelease]; |
| 202 |
| 203 [attributes addObject:NSAccessibilityValueAttribute]; |
| 204 [attributes addObject:NSAccessibilityDescriptionAttribute]; |
| 205 return [attributes autorelease]; |
| 206 } |
| 207 |
| 208 - (id)accessibilityAttributeValue:(NSString*)attribute { |
| 209 if (!hostedView_) |
| 210 return [super accessibilityAttributeValue:attribute]; |
| 211 |
| 212 ui::AXViewState state; |
| 213 hostedView_->GetAccessibleState(&state); |
| 214 switch (state.role) { |
| 215 default: |
| 216 NOTREACHED(); |
| 217 } |
| 218 #if 0 |
| 219 if ([attribute isEqual:NSAccessibilityRoleAttribute]) |
| 220 return NSAccessibilityTabGroupRole; |
| 221 if ([attribute isEqual:NSAccessibilityTabsAttribute]) { |
| 222 NSMutableArray* tabs = [[[NSMutableArray alloc] init] autorelease]; |
| 223 NSArray* children = |
| 224 [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; |
| 225 for (id child in children) { |
| 226 if ([[child accessibilityAttributeValue:NSAccessibilityRoleAttribute] |
| 227 isEqual:NSAccessibilityRadioButtonRole]) { |
| 228 [tabs addObject:child]; |
| 229 } |
| 230 } |
| 231 return tabs; |
| 232 } |
| 233 if ([attribute isEqual:NSAccessibilityContentsAttribute]) |
| 234 return [self accessibilityAttributeValue:NSAccessibilityChildrenAttribute]; |
| 235 if ([attribute isEqual:NSAccessibilityValueAttribute]) |
| 236 return [controller_ activeTabView]; |
| 237 #endif |
| 238 |
| 239 return [super accessibilityAttributeValue:attribute]; |
| 240 } |
| 241 |
| 242 @end |
OLD | NEW |