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.
5 #import "ui/views/cocoa/bridged_content_view.h"
7 #include "base/logging.h"
8 #import "base/mac/scoped_nsobject.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "ui/base/ime/text_input_client.h"
11 #include "ui/gfx/canvas_paint_mac.h"
12 #include "ui/gfx/geometry/rect.h"
13 #include "ui/strings/grit/ui_strings.h"
14 #include "ui/views/view.h"
15 #include "ui/views/widget/widget.h"
17 @interface BridgedContentView ()
19 // Translates the location of |theEvent| to toolkit-views coordinates and passes
20 // the event to NativeWidgetMac for handling.
21 - (void)handleMouseEvent:(NSEvent*)theEvent;
23 // Execute a command on the currently focused TextInputClient.
24 // |commandId| should be a resource ID from ui_strings.grd.
25 - (void)doCommandByID:(int)commandId;
29 @implementation BridgedContentView
31 @synthesize hostedView = hostedView_;
32 @synthesize textInputClient = textInputClient_;
34 - (id)initWithView:(views::View*)viewToHost {
36 gfx::Rect bounds = viewToHost->bounds();
37 // To keep things simple, assume the origin is (0, 0) until there exists a use
38 // case for something other than that.
39 DCHECK(bounds.origin().IsOrigin());
40 NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
41 if ((self = [super initWithFrame:initialFrame])) {
42 hostedView_ = viewToHost;
45 [[CrTrackingArea alloc] initWithRect:NSZeroRect
46 options:NSTrackingMouseMoved |
47 NSTrackingActiveAlways |
48 NSTrackingInVisibleRect
51 [self addTrackingArea:trackingArea_.get()];
58 [trackingArea_.get() clearOwner];
59 [self removeTrackingArea:trackingArea_.get()];
62 // BridgedContentView private implementation.
64 - (void)handleMouseEvent:(NSEvent*)theEvent {
68 ui::MouseEvent event(theEvent);
69 hostedView_->GetWidget()->OnMouseEvent(&event);
72 - (void)doCommandByID:(int)commandId {
73 if (textInputClient_ && textInputClient_->IsEditingCommandEnabled(commandId))
74 textInputClient_->ExecuteEditingCommand(commandId);
77 // NSView implementation.
79 - (BOOL)acceptsFirstResponder {
83 - (void)setFrameSize:(NSSize)newSize {
84 [super setFrameSize:newSize];
88 hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
91 - (void)drawRect:(NSRect)dirtyRect {
95 gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
96 hostedView_->Paint(&canvas, views::CullSet());
99 // NSResponder implementation.
101 - (void)keyDown:(NSEvent*)theEvent {
102 if (textInputClient_)
103 [self interpretKeyEvents:@[ theEvent ]];
105 [super keyDown:theEvent];
108 - (void)mouseDown:(NSEvent*)theEvent {
109 [self handleMouseEvent:theEvent];
112 - (void)rightMouseDown:(NSEvent*)theEvent {
113 [self handleMouseEvent:theEvent];
116 - (void)otherMouseDown:(NSEvent*)theEvent {
117 [self handleMouseEvent:theEvent];
120 - (void)mouseUp:(NSEvent*)theEvent {
121 [self handleMouseEvent:theEvent];
124 - (void)rightMouseUp:(NSEvent*)theEvent {
125 [self handleMouseEvent:theEvent];
128 - (void)otherMouseUp:(NSEvent*)theEvent {
129 [self handleMouseEvent:theEvent];
132 - (void)mouseDragged:(NSEvent*)theEvent {
133 [self handleMouseEvent:theEvent];
136 - (void)rightMouseDragged:(NSEvent*)theEvent {
137 [self handleMouseEvent:theEvent];
140 - (void)otherMouseDragged:(NSEvent*)theEvent {
141 [self handleMouseEvent:theEvent];
144 - (void)mouseMoved:(NSEvent*)theEvent {
145 // Note: mouseEntered: and mouseExited: are not handled separately.
146 // |hostedView_| is responsible for converting the move events into entered
147 // and exited events for the view heirarchy.
148 [self handleMouseEvent:theEvent];
151 - (void)scrollWheel:(NSEvent*)theEvent {
155 ui::MouseWheelEvent event(theEvent);
156 hostedView_->GetWidget()->OnMouseEvent(&event);
159 - (void)deleteBackward:(id)sender {
160 [self doCommandByID:IDS_DELETE_BACKWARD];
163 - (void)deleteForward:(id)sender {
164 [self doCommandByID:IDS_DELETE_FORWARD];
167 - (void)moveLeft:(id)sender {
168 [self doCommandByID:IDS_MOVE_LEFT];
171 - (void)moveRight:(id)sender {
172 [self doCommandByID:IDS_MOVE_RIGHT];
175 - (void)insertText:(id)text {
176 if (textInputClient_)
177 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
180 // Support for Services in context menus.
181 // Currently we only support reading and writing plain strings.
182 - (id)validRequestorForSendType:(NSString*)sendType
183 returnType:(NSString*)returnType {
184 BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
185 [self selectedRange].length > 0;
186 BOOL canRead = [returnType isEqualToString:NSStringPboardType];
187 // Valid if (sendType, returnType) is either (string, nil), (nil, string),
188 // or (string, string).
189 BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
190 (canRead && (canWrite || !sendType)));
191 return valid ? self : [super validRequestorForSendType:sendType
192 returnType:returnType];
195 // NSServicesRequests informal protocol.
197 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
198 DCHECK([types containsObject:NSStringPboardType]);
199 if (!textInputClient_)
202 gfx::Range selectionRange;
203 if (!textInputClient_->GetSelectionRange(&selectionRange))
207 textInputClient_->GetTextFromRange(selectionRange, &text);
208 return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
211 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
213 [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
214 DCHECK([objects count] == 1);
215 [self insertText:[objects lastObject]];
219 // NSTextInputClient protocol implementation.
221 - (NSAttributedString*)
222 attributedSubstringForProposedRange:(NSRange)range
223 actualRange:(NSRangePointer)actualRange {
224 base::string16 substring;
225 if (textInputClient_) {
226 gfx::Range textRange;
227 textInputClient_->GetTextRange(&textRange);
228 gfx::Range subrange = textRange.Intersect(gfx::Range(range));
229 textInputClient_->GetTextFromRange(subrange, &substring);
231 *actualRange = subrange.ToNSRange();
233 return [[[NSAttributedString alloc]
234 initWithString:base::SysUTF16ToNSString(substring)] autorelease];
237 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
242 - (void)doCommandBySelector:(SEL)selector {
243 if ([self respondsToSelector:selector])
244 [self performSelector:selector withObject:nil];
246 [[self nextResponder] doCommandBySelector:selector];
249 - (NSRect)firstRectForCharacterRange:(NSRange)range
250 actualRange:(NSRangePointer)actualRange {
255 - (BOOL)hasMarkedText {
256 return textInputClient_ && textInputClient_->HasCompositionText();
259 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
260 if (!textInputClient_)
263 if ([text isKindOfClass:[NSAttributedString class]])
264 text = [text string];
265 textInputClient_->DeleteRange(gfx::Range(replacementRange));
266 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
269 - (NSRange)markedRange {
270 if (!textInputClient_)
271 return NSMakeRange(NSNotFound, 0);
274 textInputClient_->GetCompositionTextRange(&range);
275 return range.ToNSRange();
278 - (NSRange)selectedRange {
279 if (!textInputClient_)
280 return NSMakeRange(NSNotFound, 0);
283 textInputClient_->GetSelectionRange(&range);
284 return range.ToNSRange();
287 - (void)setMarkedText:(id)text
288 selectedRange:(NSRange)selectedRange
289 replacementRange:(NSRange)replacementRange {
290 if (!textInputClient_)
293 if ([text isKindOfClass:[NSAttributedString class]])
294 text = [text string];
295 ui::CompositionText composition;
296 composition.text = base::SysNSStringToUTF16(text);
297 composition.selection = gfx::Range(selectedRange);
298 textInputClient_->SetCompositionText(composition);
302 if (textInputClient_)
303 textInputClient_->ConfirmCompositionText();
306 - (NSArray*)validAttributesForMarkedText {
310 // NSAccessibility informal protocol implementation.
312 - (id)accessibilityAttributeValue:(NSString*)attribute {
313 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
314 return @[ hostedView_->GetNativeViewAccessible() ];
317 return [super accessibilityAttributeValue:attribute];
320 - (id)accessibilityHitTest:(NSPoint)point {
321 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];