Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(311)

Side by Side Diff: ui/views/cocoa/bridged_content_view.mm

Issue 809773006: MacViews: Intercept events for Menus (after AppKit has interpreted keystrokes) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@20141205-MacViews-AcceleratedWidget-PLUS-AddingLayers-fromcl-PLUS-bringup
Patch Set: A few more cleanups Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/events/test/cocoa_test_event_utils.mm ('k') | ui/views/cocoa/native_widget_mac_nswindow.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ui/views/cocoa/bridged_content_view.h" 5 #import "ui/views/cocoa/bridged_content_view.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #import "base/mac/scoped_nsobject.h" 8 #import "base/mac/scoped_nsobject.h"
9 #include "base/strings/sys_string_conversions.h" 9 #include "base/strings/sys_string_conversions.h"
10 #include "ui/base/ime/text_input_client.h" 10 #include "ui/base/ime/text_input_client.h"
11 #import "ui/events/cocoa/cocoa_event_utils.h"
12 #include "ui/events/keycodes/dom3/dom_code.h"
13 #import "ui/events/keycodes/keyboard_code_conversion_mac.h"
11 #include "ui/gfx/canvas_paint_mac.h" 14 #include "ui/gfx/canvas_paint_mac.h"
12 #include "ui/gfx/geometry/rect.h" 15 #include "ui/gfx/geometry/rect.h"
13 #include "ui/strings/grit/ui_strings.h" 16 #include "ui/strings/grit/ui_strings.h"
17 #include "ui/views/controls/menu/menu_controller.h"
14 #include "ui/views/view.h" 18 #include "ui/views/view.h"
15 #include "ui/views/widget/widget.h" 19 #include "ui/views/widget/widget.h"
16 20
21 using views::MenuController;
22
17 namespace { 23 namespace {
18 24
19 // Convert a |point| in |source_window|'s AppKit coordinate system (origin at 25 // Convert a |point| in |source_window|'s AppKit coordinate system (origin at
20 // the bottom left of the window) to |target_window|'s content rect, with the 26 // the bottom left of the window) to |target_window|'s content rect, with the
21 // origin at the top left of the content area. 27 // origin at the top left of the content area.
22 // If |source_window| is nil, |point| will be treated as screen coordinates. 28 // If |source_window| is nil, |point| will be treated as screen coordinates.
23 gfx::Point MovePointToWindow(const NSPoint& point, 29 gfx::Point MovePointToWindow(const NSPoint& point,
24 NSWindow* source_window, 30 NSWindow* source_window,
25 NSWindow* target_window) { 31 NSWindow* target_window) {
26 NSPoint point_in_screen = source_window 32 NSPoint point_in_screen = source_window
27 ? [source_window convertBaseToScreen:point] 33 ? [source_window convertBaseToScreen:point]
28 : point; 34 : point;
29 35
30 NSPoint point_in_window = [target_window convertScreenToBase:point_in_screen]; 36 NSPoint point_in_window = [target_window convertScreenToBase:point_in_screen];
31 NSRect content_rect = 37 NSRect content_rect =
32 [target_window contentRectForFrameRect:[target_window frame]]; 38 [target_window contentRectForFrameRect:[target_window frame]];
33 return gfx::Point(point_in_window.x, 39 return gfx::Point(point_in_window.x,
34 NSHeight(content_rect) - point_in_window.y); 40 NSHeight(content_rect) - point_in_window.y);
35 } 41 }
36 42
37 } 43 }
38 44
39 @interface BridgedContentView () 45 @interface BridgedContentView ()
40 46
41 // Translates the location of |theEvent| to toolkit-views coordinates and passes 47 // Translates the location of |theEvent| to toolkit-views coordinates and passes
42 // the event to NativeWidgetMac for handling. 48 // the event to NativeWidgetMac for handling.
43 - (void)handleMouseEvent:(NSEvent*)theEvent; 49 - (void)handleMouseEvent:(NSEvent*)theEvent;
44 50
45 // Execute a command on the currently focused TextInputClient. 51 // Handles an NSResponder Action Message by mapping it to a corresponding text
46 // |commandId| should be a resource ID from ui_strings.grd. 52 // editing command from ui_strings.grd and, when not being sent to a
47 - (void)doCommandByID:(int)commandId; 53 // TextInputClient, the keyCode that toolkit-views expects internally.
54 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales
55 // even though the Home key on Mac defaults to moveToBeginningOfDocument:.
56 // This approach also allows action messages a user
57 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be
58 // catered for.
59 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict
60 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do
61 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to
62 // get something readable.
63 - (void)handleAction:(int)commandId
64 keyCode:(ui::KeyboardCode)keyCode
65 domCode:(ui::DomCode)domCode
66 eventFlags:(int)eventFlags;
48 67
49 @end 68 @end
50 69
51 @implementation BridgedContentView 70 @implementation BridgedContentView
52 71
53 @synthesize hostedView = hostedView_; 72 @synthesize hostedView = hostedView_;
54 @synthesize textInputClient = textInputClient_; 73 @synthesize textInputClient = textInputClient_;
55 74
56 - (id)initWithView:(views::View*)viewToHost { 75 - (id)initWithView:(views::View*)viewToHost {
57 DCHECK(viewToHost); 76 DCHECK(viewToHost);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 // BridgedContentView private implementation. 123 // BridgedContentView private implementation.
105 124
106 - (void)handleMouseEvent:(NSEvent*)theEvent { 125 - (void)handleMouseEvent:(NSEvent*)theEvent {
107 if (!hostedView_) 126 if (!hostedView_)
108 return; 127 return;
109 128
110 ui::MouseEvent event(theEvent); 129 ui::MouseEvent event(theEvent);
111 hostedView_->GetWidget()->OnMouseEvent(&event); 130 hostedView_->GetWidget()->OnMouseEvent(&event);
112 } 131 }
113 132
114 - (void)doCommandByID:(int)commandId { 133 - (void)handleAction:(int)commandId
115 if (textInputClient_ && textInputClient_->IsEditingCommandEnabled(commandId)) 134 keyCode:(ui::KeyboardCode)keyCode
135 domCode:(ui::DomCode)domCode
136 eventFlags:(int)eventFlags {
137 if (!hostedView_)
138 return;
139
140 // If there's an active MenuController it gets preference, and it will likely
141 // swallow the event.
142 MenuController* menuController = MenuController::GetActiveInstance();
143 if (menuController && menuController->owner() == hostedView_->GetWidget()) {
144 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
145 if (menuController->OnWillDispatchEvent(event) == ui::POST_DISPATCH_NONE)
146 return;
147 }
148
149 // If there's an active TextInputClient, it ignores the key and processes the
150 // logical editing action.
151 if (commandId && textInputClient_ &&
152 textInputClient_->IsEditingCommandEnabled(commandId)) {
116 textInputClient_->ExecuteEditingCommand(commandId); 153 textInputClient_->ExecuteEditingCommand(commandId);
154 return;
155 }
156
157 // Otherwise, process the action as a regular key event.
158 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
159 hostedView_->GetWidget()->OnKeyEvent(&event);
117 } 160 }
118 161
119 // NSView implementation. 162 // NSView implementation.
120 163
121 - (BOOL)acceptsFirstResponder { 164 - (BOOL)acceptsFirstResponder {
122 return YES; 165 return YES;
123 } 166 }
124 167
125 - (void)setFrameSize:(NSSize)newSize { 168 - (void)setFrameSize:(NSSize)newSize {
126 [super setFrameSize:newSize]; 169 [super setFrameSize:newSize];
(...skipping 10 matching lines...) Expand all
137 return; 180 return;
138 181
139 // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer(). 182 // If there's a layer, painting occurs in BridgedNativeWidget::OnPaintLayer().
140 if (hostedView_->GetWidget()->GetLayer()) 183 if (hostedView_->GetWidget()->GetLayer())
141 return; 184 return;
142 185
143 gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */); 186 gfx::CanvasSkiaPaint canvas(dirtyRect, false /* opaque */);
144 hostedView_->GetWidget()->OnNativeWidgetPaint(&canvas); 187 hostedView_->GetWidget()->OnNativeWidgetPaint(&canvas);
145 } 188 }
146 189
190 - (NSTextInputContext*)inputContext {
191 if (!hostedView_)
192 return [super inputContext];
193
194 // If a menu is active, and -[NSView interpretKeyEvents:] asks for the
195 // input context, return nil. This ensures the action message is sent to
196 // the view, rather than any NSTextInputClient a subview has installed.
197 MenuController* menuController = MenuController::GetActiveInstance();
198 if (menuController && menuController->owner() == hostedView_->GetWidget())
199 return nil;
200
201 return [super inputContext];
202 }
203
147 // NSResponder implementation. 204 // NSResponder implementation.
148 205
149 - (void)keyDown:(NSEvent*)theEvent { 206 - (void)keyDown:(NSEvent*)theEvent {
150 if (textInputClient_) 207 // Convert the event into an action message, according to OSX key mappings.
151 [self interpretKeyEvents:@[ theEvent ]]; 208 [self interpretKeyEvents:@[ theEvent ]];
152 else
153 [super keyDown:theEvent];
154 } 209 }
155 210
156 - (void)mouseDown:(NSEvent*)theEvent { 211 - (void)mouseDown:(NSEvent*)theEvent {
157 [self handleMouseEvent:theEvent]; 212 [self handleMouseEvent:theEvent];
158 } 213 }
159 214
160 - (void)rightMouseDown:(NSEvent*)theEvent { 215 - (void)rightMouseDown:(NSEvent*)theEvent {
161 [self handleMouseEvent:theEvent]; 216 [self handleMouseEvent:theEvent];
162 } 217 }
163 218
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
197 } 252 }
198 253
199 - (void)scrollWheel:(NSEvent*)theEvent { 254 - (void)scrollWheel:(NSEvent*)theEvent {
200 if (!hostedView_) 255 if (!hostedView_)
201 return; 256 return;
202 257
203 ui::MouseWheelEvent event(theEvent); 258 ui::MouseWheelEvent event(theEvent);
204 hostedView_->GetWidget()->OnMouseEvent(&event); 259 hostedView_->GetWidget()->OnMouseEvent(&event);
205 } 260 }
206 261
207 - (void)deleteBackward:(id)sender { 262 ////////////////////////////////////////////////////////////////////////////////
208 [self doCommandByID:IDS_DELETE_BACKWARD]; 263 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the
264 // 10.9 SDK). The list should eventually be complete. Anything not defined will
265 // beep when interpretKeyEvents: would otherwise call it.
266 // TODO(tapted): Make this list complete.
267
268 // The insertText action message forwards to the TextInputClient unless a menu
269 // is active.
270 - (void)insertText:(id)text {
271 [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)];
209 } 272 }
210 273
211 - (void)deleteForward:(id)sender { 274 // Selection movement and scrolling.
212 [self doCommandByID:IDS_DELETE_FORWARD]; 275
276 - (void)moveRight:(id)sender {
277 [self handleAction:IDS_MOVE_RIGHT
278 keyCode:ui::VKEY_RIGHT
279 domCode:ui::DomCode::ARROW_RIGHT
280 eventFlags:0];
213 } 281 }
214 282
215 - (void)moveLeft:(id)sender { 283 - (void)moveLeft:(id)sender {
216 [self doCommandByID:IDS_MOVE_LEFT]; 284 [self handleAction:IDS_MOVE_LEFT
285 keyCode:ui::VKEY_LEFT
286 domCode:ui::DomCode::ARROW_LEFT
287 eventFlags:0];
217 } 288 }
218 289
219 - (void)moveRight:(id)sender { 290 - (void)moveUp:(id)sender {
220 [self doCommandByID:IDS_MOVE_RIGHT]; 291 [self handleAction:0
292 keyCode:ui::VKEY_UP
293 domCode:ui::DomCode::ARROW_UP
294 eventFlags:0];
221 } 295 }
222 296
223 - (void)insertText:(id)text { 297 - (void)moveDown:(id)sender {
224 if (textInputClient_) 298 [self handleAction:0
225 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); 299 keyCode:ui::VKEY_DOWN
300 domCode:ui::DomCode::ARROW_DOWN
301 eventFlags:0];
302 }
303
304 - (void)moveWordRight:(id)sender {
305 [self handleAction:IDS_MOVE_WORD_RIGHT
306 keyCode:ui::VKEY_RIGHT
307 domCode:ui::DomCode::ARROW_RIGHT
308 eventFlags:ui::EF_CONTROL_DOWN];
309 }
310
311 - (void)moveWordLeft:(id)sender {
312 [self handleAction:IDS_MOVE_WORD_LEFT
313 keyCode:ui::VKEY_LEFT
314 domCode:ui::DomCode::ARROW_LEFT
315 eventFlags:ui::EF_CONTROL_DOWN];
316 }
317
318 - (void)moveLeftAndModifySelection:(id)sender {
319 [self handleAction:IDS_MOVE_LEFT_AND_MODIFY_SELECTION
320 keyCode:ui::VKEY_LEFT
321 domCode:ui::DomCode::ARROW_LEFT
322 eventFlags:ui::EF_SHIFT_DOWN];
323 }
324
325 - (void)moveRightAndModifySelection:(id)sender {
326 [self handleAction:IDS_MOVE_RIGHT_AND_MODIFY_SELECTION
327 keyCode:ui::VKEY_RIGHT
328 domCode:ui::DomCode::ARROW_RIGHT
329 eventFlags:ui::EF_SHIFT_DOWN];
330 }
331
332 - (void)moveWordRightAndModifySelection:(id)sender {
333 [self handleAction:IDS_MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
334 keyCode:ui::VKEY_RIGHT
335 domCode:ui::DomCode::ARROW_RIGHT
336 eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
337 }
338
339 - (void)moveWordLeftAndModifySelection:(id)sender {
340 [self handleAction:IDS_MOVE_WORD_LEFT_AND_MODIFY_SELECTION
341 keyCode:ui::VKEY_LEFT
342 domCode:ui::DomCode::ARROW_LEFT
343 eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN];
344 }
345
346 - (void)moveToLeftEndOfLine:(id)sender {
347 [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE
348 keyCode:ui::VKEY_HOME
349 domCode:ui::DomCode::HOME
350 eventFlags:0];
351 }
352
353 - (void)moveToRightEndOfLine:(id)sender {
354 [self handleAction:IDS_MOVE_TO_END_OF_LINE
355 keyCode:ui::VKEY_END
356 domCode:ui::DomCode::END
357 eventFlags:0];
358 }
359
360 - (void)moveToLeftEndOfLineAndModifySelection:(id)sender {
361 [self handleAction:IDS_MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION
362 keyCode:ui::VKEY_HOME
363 domCode:ui::DomCode::HOME
364 eventFlags:ui::EF_SHIFT_DOWN];
365 }
366
367 - (void)moveToRightEndOfLineAndModifySelection:(id)sender {
368 [self handleAction:IDS_MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
369 keyCode:ui::VKEY_END
370 domCode:ui::DomCode::END
371 eventFlags:ui::EF_SHIFT_DOWN];
372 }
373
374 // Insertions and Indentations.
375
376 - (void)insertNewline:(id)sender {
377 [self handleAction:0
378 keyCode:ui::VKEY_RETURN
379 domCode:ui::DomCode::ENTER
380 eventFlags:0];
381 }
382
383 // Deletions.
384
385 - (void)deleteForward:(id)sender {
386 [self handleAction:IDS_DELETE_FORWARD
387 keyCode:ui::VKEY_DELETE
388 domCode:ui::DomCode::DEL
389 eventFlags:0];
390 }
391
392 - (void)deleteBackward:(id)sender {
393 [self handleAction:IDS_DELETE_BACKWARD
394 keyCode:ui::VKEY_BACK
395 domCode:ui::DomCode::BACKSPACE
396 eventFlags:0];
397 }
398
399 - (void)deleteWordForward:(id)sender {
400 [self handleAction:IDS_DELETE_WORD_FORWARD
401 keyCode:ui::VKEY_DELETE
402 domCode:ui::DomCode::DEL
403 eventFlags:ui::EF_CONTROL_DOWN];
404 }
405
406 - (void)deleteWordBackward:(id)sender {
407 [self handleAction:IDS_DELETE_WORD_BACKWARD
408 keyCode:ui::VKEY_BACK
409 domCode:ui::DomCode::BACKSPACE
410 eventFlags:ui::EF_CONTROL_DOWN];
411 }
412
413 // Cancellation.
414
415 - (void)cancelOperation:(id)sender {
416 [self handleAction:0
417 keyCode:ui::VKEY_ESCAPE
418 domCode:ui::DomCode::ESCAPE
419 eventFlags:0];
226 } 420 }
227 421
228 // Support for Services in context menus. 422 // Support for Services in context menus.
229 // Currently we only support reading and writing plain strings. 423 // Currently we only support reading and writing plain strings.
230 - (id)validRequestorForSendType:(NSString*)sendType 424 - (id)validRequestorForSendType:(NSString*)sendType
231 returnType:(NSString*)returnType { 425 returnType:(NSString*)returnType {
232 BOOL canWrite = [sendType isEqualToString:NSStringPboardType] && 426 BOOL canWrite = [sendType isEqualToString:NSStringPboardType] &&
233 [self selectedRange].length > 0; 427 [self selectedRange].length > 0;
234 BOOL canRead = [returnType isEqualToString:NSStringPboardType]; 428 BOOL canRead = [returnType isEqualToString:NSStringPboardType];
235 // Valid if (sendType, returnType) is either (string, nil), (nil, string), 429 // Valid if (sendType, returnType) is either (string, nil), (nil, string),
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 actualRange:(NSRangePointer)actualRange { 492 actualRange:(NSRangePointer)actualRange {
299 NOTIMPLEMENTED(); 493 NOTIMPLEMENTED();
300 return NSZeroRect; 494 return NSZeroRect;
301 } 495 }
302 496
303 - (BOOL)hasMarkedText { 497 - (BOOL)hasMarkedText {
304 return textInputClient_ && textInputClient_->HasCompositionText(); 498 return textInputClient_ && textInputClient_->HasCompositionText();
305 } 499 }
306 500
307 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { 501 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
308 if (!textInputClient_) 502 if (!hostedView_)
309 return; 503 return;
310 504
311 if ([text isKindOfClass:[NSAttributedString class]]) 505 if ([text isKindOfClass:[NSAttributedString class]])
312 text = [text string]; 506 text = [text string];
507
508 MenuController* menuController = MenuController::GetActiveInstance();
509 if (menuController && menuController->owner() == hostedView_->GetWidget()) {
510 // Handle menu mnemonics (e.g. "sav" jumps to "Save").
511 for (NSUInteger i = 0; i < [text length]; ++i) {
512 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::KeyboardCodeFromCharCode(
513 [text characterAtIndex:i]),
514 ui::DomCode::NONE,
515 ui::EventFlagsFromModifiers([NSEvent modifierFlags]));
516 if (menuController->OnWillDispatchEvent(event) == ui::POST_DISPATCH_NONE)
517 return;
518 }
519 }
520
521 if (!textInputClient_)
522 return;
523
313 textInputClient_->DeleteRange(gfx::Range(replacementRange)); 524 textInputClient_->DeleteRange(gfx::Range(replacementRange));
314 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); 525 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
315 } 526 }
316 527
317 - (NSRange)markedRange { 528 - (NSRange)markedRange {
318 if (!textInputClient_) 529 if (!textInputClient_)
319 return NSMakeRange(NSNotFound, 0); 530 return NSMakeRange(NSNotFound, 0);
320 531
321 gfx::Range range; 532 gfx::Range range;
322 textInputClient_->GetCompositionTextRange(&range); 533 textInputClient_->GetCompositionTextRange(&range);
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
363 } 574 }
364 575
365 return [super accessibilityAttributeValue:attribute]; 576 return [super accessibilityAttributeValue:attribute];
366 } 577 }
367 578
368 - (id)accessibilityHitTest:(NSPoint)point { 579 - (id)accessibilityHitTest:(NSPoint)point {
369 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; 580 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
370 } 581 }
371 582
372 @end 583 @end
OLDNEW
« no previous file with comments | « ui/events/test/cocoa_test_event_utils.mm ('k') | ui/views/cocoa/native_widget_mac_nswindow.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698