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_;
33 @synthesize willShow = willShow_;
35 - (id)initWithView:(views::View*)viewToHost {
37 gfx::Rect bounds = viewToHost->bounds();
38 // To keep things simple, assume the origin is (0, 0) until there exists a use
39 // case for something other than that.
40 DCHECK(bounds.origin().IsOrigin());
41 NSRect initialFrame = NSMakeRect(0, 0, bounds.width(), bounds.height());
42 if ((self = [super initWithFrame:initialFrame])) {
43 hostedView_ = viewToHost;
46 [[CrTrackingArea alloc] initWithRect:NSZeroRect
47 options:NSTrackingMouseMoved |
48 NSTrackingActiveAlways |
49 NSTrackingInVisibleRect
52 [self addTrackingArea:trackingArea_.get()];
59 [trackingArea_.get() clearOwner];
60 [self removeTrackingArea:trackingArea_.get()];
63 // BridgedContentView private implementation.
65 - (void)handleMouseEvent:(NSEvent*)theEvent {
69 ui::MouseEvent event(theEvent);
70 hostedView_->GetWidget()->OnMouseEvent(&event);
73 - (void)doCommandByID:(int)commandId {
74 if (textInputClient_ && textInputClient_->IsEditingCommandEnabled(commandId))
75 textInputClient_->ExecuteEditingCommand(commandId);
78 // NSView implementation.
80 - (BOOL)acceptsFirstResponder {
84 - (void)setFrameSize:(NSSize)newSize {
85 [super setFrameSize:newSize];
89 hostedView_->SetSize(gfx::Size(newSize.width, newSize.height));
92 - (void)drawRect:(NSRect)dirtyRect {
93 // Note that on a Show, Cocoa calls drawRect: before changing
94 // -[NSWindow isVisible], hence the extra check.
95 if (!hostedView_ || (!willShow_ && ![[self window] isVisible]))
98 gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
99 hostedView_->GetWidget()->OnNativeWidgetPaint(&canvas);
102 // NSResponder implementation.
104 - (void)keyDown:(NSEvent*)theEvent {
105 if (textInputClient_)
106 [self interpretKeyEvents:@[ theEvent ]];
108 [super keyDown:theEvent];
111 - (void)mouseDown:(NSEvent*)theEvent {
112 [self handleMouseEvent:theEvent];
115 - (void)rightMouseDown:(NSEvent*)theEvent {
116 [self handleMouseEvent:theEvent];
119 - (void)otherMouseDown:(NSEvent*)theEvent {
120 [self handleMouseEvent:theEvent];
123 - (void)mouseUp:(NSEvent*)theEvent {
124 [self handleMouseEvent:theEvent];
127 - (void)rightMouseUp:(NSEvent*)theEvent {
128 [self handleMouseEvent:theEvent];
131 - (void)otherMouseUp:(NSEvent*)theEvent {
132 [self handleMouseEvent:theEvent];
135 - (void)mouseDragged:(NSEvent*)theEvent {
136 [self handleMouseEvent:theEvent];
139 - (void)rightMouseDragged:(NSEvent*)theEvent {
140 [self handleMouseEvent:theEvent];
143 - (void)otherMouseDragged:(NSEvent*)theEvent {
144 [self handleMouseEvent:theEvent];
147 - (void)mouseMoved:(NSEvent*)theEvent {
148 // Note: mouseEntered: and mouseExited: are not handled separately.
149 // |hostedView_| is responsible for converting the move events into entered
150 // and exited events for the view heirarchy.
151 [self handleMouseEvent:theEvent];
154 - (void)scrollWheel:(NSEvent*)theEvent {
158 ui::MouseWheelEvent event(theEvent);
159 hostedView_->GetWidget()->OnMouseEvent(&event);
162 - (void)deleteBackward:(id)sender {
163 [self doCommandByID:IDS_DELETE_BACKWARD];
166 - (void)deleteForward:(id)sender {
167 [self doCommandByID:IDS_DELETE_FORWARD];
170 - (void)moveLeft:(id)sender {
171 [self doCommandByID:IDS_MOVE_LEFT];
174 - (void)moveRight:(id)sender {
175 [self doCommandByID:IDS_MOVE_RIGHT];
178 - (void)insertText:(id)text {
179 if (textInputClient_)
180 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
183 // Support for Services in context menus.
184 // Currently we only support reading and writing plain strings.
185 - (id)validRequestorForSendType:(NSString*)sendType
186 returnType:(NSString*)returnType {
187 BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
188 [self selectedRange].length > 0;
189 BOOL canRead = [returnType isEqualToString:NSStringPboardType];
190 // Valid if (sendType, returnType) is either (string, nil), (nil, string),
191 // or (string, string).
192 BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
193 (canRead && (canWrite || !sendType)));
194 return valid ? self : [super validRequestorForSendType:sendType
195 returnType:returnType];
198 // NSServicesRequests informal protocol.
200 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
201 DCHECK([types containsObject:NSStringPboardType]);
202 if (!textInputClient_)
205 gfx::Range selectionRange;
206 if (!textInputClient_->GetSelectionRange(&selectionRange))
210 textInputClient_->GetTextFromRange(selectionRange, &text);
211 return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
214 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
216 [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
217 DCHECK([objects count] == 1);
218 [self insertText:[objects lastObject]];
222 // NSTextInputClient protocol implementation.
224 - (NSAttributedString*)
225 attributedSubstringForProposedRange:(NSRange)range
226 actualRange:(NSRangePointer)actualRange {
227 base::string16 substring;
228 if (textInputClient_) {
229 gfx::Range textRange;
230 textInputClient_->GetTextRange(&textRange);
231 gfx::Range subrange = textRange.Intersect(gfx::Range(range));
232 textInputClient_->GetTextFromRange(subrange, &substring);
234 *actualRange = subrange.ToNSRange();
236 return [[[NSAttributedString alloc]
237 initWithString:base::SysUTF16ToNSString(substring)] autorelease];
240 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
245 - (void)doCommandBySelector:(SEL)selector {
246 if ([self respondsToSelector:selector])
247 [self performSelector:selector withObject:nil];
249 [[self nextResponder] doCommandBySelector:selector];
252 - (NSRect)firstRectForCharacterRange:(NSRange)range
253 actualRange:(NSRangePointer)actualRange {
258 - (BOOL)hasMarkedText {
259 return textInputClient_ && textInputClient_->HasCompositionText();
262 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
263 if (!textInputClient_)
266 if ([text isKindOfClass:[NSAttributedString class]])
267 text = [text string];
268 textInputClient_->DeleteRange(gfx::Range(replacementRange));
269 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
272 - (NSRange)markedRange {
273 if (!textInputClient_)
274 return NSMakeRange(NSNotFound, 0);
277 textInputClient_->GetCompositionTextRange(&range);
278 return range.ToNSRange();
281 - (NSRange)selectedRange {
282 if (!textInputClient_)
283 return NSMakeRange(NSNotFound, 0);
286 textInputClient_->GetSelectionRange(&range);
287 return range.ToNSRange();
290 - (void)setMarkedText:(id)text
291 selectedRange:(NSRange)selectedRange
292 replacementRange:(NSRange)replacementRange {
293 if (!textInputClient_)
296 if ([text isKindOfClass:[NSAttributedString class]])
297 text = [text string];
298 ui::CompositionText composition;
299 composition.text = base::SysNSStringToUTF16(text);
300 composition.selection = gfx::Range(selectedRange);
301 textInputClient_->SetCompositionText(composition);
305 if (textInputClient_)
306 textInputClient_->ConfirmCompositionText();
309 - (NSArray*)validAttributesForMarkedText {
313 // NSAccessibility informal protocol implementation.
315 - (id)accessibilityAttributeValue:(NSString*)attribute {
316 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
317 return @[ hostedView_->GetNativeViewAccessible() ];
320 return [super accessibilityAttributeValue:attribute];
323 - (id)accessibilityHitTest:(NSPoint)point {
324 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];