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 {
152 [self handleMouseEvent:theEvent];
155 - (void)deleteBackward:(id)sender {
156 [self doCommandByID:IDS_DELETE_BACKWARD];
159 - (void)deleteForward:(id)sender {
160 [self doCommandByID:IDS_DELETE_FORWARD];
163 - (void)moveLeft:(id)sender {
164 [self doCommandByID:IDS_MOVE_LEFT];
167 - (void)moveRight:(id)sender {
168 [self doCommandByID:IDS_MOVE_RIGHT];
171 - (void)insertText:(id)text {
172 if (textInputClient_)
173 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
176 // Support for Services in context menus.
177 // Currently we only support reading and writing plain strings.
178 - (id)validRequestorForSendType:(NSString*)sendType
179 returnType:(NSString*)returnType {
180 BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
181 [self selectedRange].length > 0;
182 BOOL canRead = [returnType isEqualToString:NSStringPboardType];
183 // Valid if (sendType, returnType) is either (string, nil), (nil, string),
184 // or (string, string).
185 BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) ||
186 (canRead && (canWrite || !sendType)));
187 return valid ? self : [super validRequestorForSendType:sendType
188 returnType:returnType];
191 // NSServicesRequests informal protocol.
193 - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types {
194 DCHECK([types containsObject:NSStringPboardType]);
195 if (!textInputClient_)
198 gfx::Range selectionRange;
199 if (!textInputClient_->GetSelectionRange(&selectionRange))
203 textInputClient_->GetTextFromRange(selectionRange, &text);
204 return [pboard writeObjects:@[ base::SysUTF16ToNSString(text) ]];
207 - (BOOL)readSelectionFromPasteboard:(NSPasteboard*)pboard {
209 [pboard readObjectsForClasses:@[ [NSString class] ] options:0];
210 DCHECK([objects count] == 1);
211 [self insertText:[objects lastObject]];
215 // NSTextInputClient protocol implementation.
217 - (NSAttributedString*)
218 attributedSubstringForProposedRange:(NSRange)range
219 actualRange:(NSRangePointer)actualRange {
220 base::string16 substring;
221 if (textInputClient_) {
222 gfx::Range textRange;
223 textInputClient_->GetTextRange(&textRange);
224 gfx::Range subrange = textRange.Intersect(gfx::Range(range));
225 textInputClient_->GetTextFromRange(subrange, &substring);
227 *actualRange = subrange.ToNSRange();
229 return [[[NSAttributedString alloc]
230 initWithString:base::SysUTF16ToNSString(substring)] autorelease];
233 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
238 - (void)doCommandBySelector:(SEL)selector {
239 if ([self respondsToSelector:selector])
240 [self performSelector:selector withObject:nil];
242 [[self nextResponder] doCommandBySelector:selector];
245 - (NSRect)firstRectForCharacterRange:(NSRange)range
246 actualRange:(NSRangePointer)actualRange {
251 - (BOOL)hasMarkedText {
252 return textInputClient_ && textInputClient_->HasCompositionText();
255 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
256 if (!textInputClient_)
259 if ([text isKindOfClass:[NSAttributedString class]])
260 text = [text string];
261 textInputClient_->DeleteRange(gfx::Range(replacementRange));
262 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
265 - (NSRange)markedRange {
266 if (!textInputClient_)
267 return NSMakeRange(NSNotFound, 0);
270 textInputClient_->GetCompositionTextRange(&range);
271 return range.ToNSRange();
274 - (NSRange)selectedRange {
275 if (!textInputClient_)
276 return NSMakeRange(NSNotFound, 0);
279 textInputClient_->GetSelectionRange(&range);
280 return range.ToNSRange();
283 - (void)setMarkedText:(id)text
284 selectedRange:(NSRange)selectedRange
285 replacementRange:(NSRange)replacementRange {
286 if (!textInputClient_)
289 if ([text isKindOfClass:[NSAttributedString class]])
290 text = [text string];
291 ui::CompositionText composition;
292 composition.text = base::SysNSStringToUTF16(text);
293 composition.selection = gfx::Range(selectedRange);
294 textInputClient_->SetCompositionText(composition);
298 if (textInputClient_)
299 textInputClient_->ConfirmCompositionText();
302 - (NSArray*)validAttributesForMarkedText {
306 // NSAccessibility informal protocol implementation.
308 - (id)accessibilityAttributeValue:(NSString*)attribute {
309 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
310 return @[ hostedView_->GetNativeViewAccessible() ];
313 return [super accessibilityAttributeValue:attribute];
316 - (id)accessibilityHitTest:(NSPoint)point {
317 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];