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

Side by Side Diff: chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.mm

Issue 2395233005: [Mac] Preserve original selection when suggesting completions with diacritics (Closed)
Patch Set: s/ASSERT/EXPECT Created 4 years, 2 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
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h" 5 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
6 6
7 #include "base/i18n/break_iterator.h"
7 #include "base/mac/sdk_forward_declarations.h" 8 #include "base/mac/sdk_forward_declarations.h"
8 #include "base/strings/string_util.h" 9 #include "base/strings/string_util.h"
9 #include "base/strings/sys_string_conversions.h" 10 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/app/chrome_command_ids.h" // IDC_* 11 #include "chrome/app/chrome_command_ids.h" // IDC_*
11 #include "chrome/browser/themes/theme_service.h" 12 #include "chrome/browser/themes/theme_service.h"
12 #include "chrome/browser/ui/browser_list.h" 13 #include "chrome/browser/ui/browser_list.h"
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h" 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
14 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h" 15 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field.h"
15 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h" 16 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_cell.h"
16 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" 17 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
(...skipping 25 matching lines...) Expand all
42 BOOL ThePasteboardIsTooDamnBig() { 43 BOOL ThePasteboardIsTooDamnBig() {
43 NSPasteboard* pb = [NSPasteboard generalPasteboard]; 44 NSPasteboard* pb = [NSPasteboard generalPasteboard];
44 NSString* type = 45 NSString* type =
45 [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; 46 [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]];
46 if (!type) 47 if (!type)
47 return NO; 48 return NO;
48 49
49 return [[pb stringForType:type] length] > kMaxPasteLength; 50 return [[pb stringForType:type] length] > kMaxPasteLength;
50 } 51 }
51 52
53 NSRange VisualSelectionRangeFromRange(NSRange range, NSString* string) {
54 if (range.location >= string.length) {
55 return range;
56 }
57 base::string16 text = base::SysNSStringToUTF16(string);
58 // TODO(lgrey): Should the break iterator be cached?
erikchen 2016/10/12 22:37:48 sounds like premature optimization to me.
lgrey 2016/10/13 15:42:52 Removed comment
59 base::i18n::BreakIterator grapheme_iterator(
60 text, base::i18n::BreakIterator::BREAK_CHARACTER);
61 if (!grapheme_iterator.Init()) {
62 return range;
63 }
64 while (range.location < text.length() &&
erikchen 2016/10/12 22:37:48 interesting. This works because NSString's interna
lgrey 2016/10/13 15:42:52 Added a comment. I could also change it to |string
erikchen 2016/10/13 17:08:28 Comments seems good enough.
65 !grapheme_iterator.IsGraphemeBoundary(
66 static_cast<size_t>(range.location))) {
67 range.location++;
68 if (range.length > 0) {
69 range.length--;
70 }
71 }
72 return range;
73 }
74
52 } // namespace 75 } // namespace
53 76
77 // Method exposed for the purpose of overriding.
78 // Used to restore model's selection range when the view doesn't
79 // match the model due to combining characters.
80 //
81 // In some cases, (completing 'y' to 'ÿour', for example), the autocomplete
82 // system requests a selection range that begins on a combining character.
83 // setSelectedRange: and friends document that the range passed to them
84 // "must begin and end on glyph boundaries and not split base glyphs and
85 // their nonspacing marks". If passed such a range, the selection is
86 // expanded to include the original user input, preventing the user
87 // from being able to type other words beginning with that letter.
88 //
89 // To resolve this, the field editor modifies the selection to start
90 // on the next glyph boundary, then keeps track of the original and
91 // modified selections, substituting the original when the user takes
92 // actions that operate on the selection. Since there are many methods
93 // in NSResponder (for example deleteToBeginningOfLine:) that operate
94 // on the selection, rather than shimming them all, we override this
95 // private method that they're implemented in terms of.
96 //
97 @interface NSTextView (PrivateTextEditing)
98 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string;
99 @end
100
54 @interface AutocompleteTextFieldEditor ()<NSDraggingSource> 101 @interface AutocompleteTextFieldEditor ()<NSDraggingSource>
55 @end 102 @end
56 103
57 @implementation AutocompleteTextFieldEditor 104 @implementation AutocompleteTextFieldEditor
58 105
59 - (BOOL)shouldDrawInsertionPoint { 106 - (BOOL)shouldDrawInsertionPoint {
60 return [super shouldDrawInsertionPoint] && 107 return [super shouldDrawInsertionPoint] &&
61 ![[[self delegate] cell] hideFocusState]; 108 ![[[self delegate] cell] hideFocusState];
62 } 109 }
63 110
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 // (URLDropTarget protocol) 440 // (URLDropTarget protocol)
394 - (void)draggingExited:(id<NSDraggingInfo>)sender { 441 - (void)draggingExited:(id<NSDraggingInfo>)sender {
395 return [dropHandler_ draggingExited:sender]; 442 return [dropHandler_ draggingExited:sender];
396 } 443 }
397 444
398 // (URLDropTarget protocol) 445 // (URLDropTarget protocol)
399 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { 446 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
400 return [dropHandler_ performDragOperation:sender]; 447 return [dropHandler_ performDragOperation:sender];
401 } 448 }
402 449
450 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string {
451 if (NSEqualRanges(visualSelectionRange_, range) &&
452 !NSEqualRanges(visualSelectionRange_, actualSelectionRange_)) {
453 range = actualSelectionRange_;
454 }
455 [super _userReplaceRange:range withString:string];
456 }
457
403 // Prevent control characters from being entered into the Omnibox. 458 // Prevent control characters from being entered into the Omnibox.
404 // This is invoked for keyboard entry, not for pasting. 459 // This is invoked for keyboard entry, not for pasting.
405 - (void)insertText:(id)aString { 460 - (void)insertText:(id)aString {
406 AutocompleteTextFieldObserver* observer = [self observer]; 461 AutocompleteTextFieldObserver* observer = [self observer];
407 if (observer) 462 if (observer)
408 observer->OnInsertText(); 463 observer->OnInsertText();
409 464
410 // Repeatedly remove control characters. The loop will only ever 465 // Repeatedly remove control characters. The loop will only ever
411 // execute at all when the user enters control characters (using 466 // execute at all when the user enters control characters (using
412 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would 467 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would
(...skipping 11 matching lines...) Expand all
424 } else { 479 } else {
425 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; 480 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_];
426 while (range.location != NSNotFound) { 481 while (range.location != NSNotFound) {
427 aString = 482 aString =
428 [aString stringByReplacingCharactersInRange:range withString:@""]; 483 [aString stringByReplacingCharactersInRange:range withString:@""];
429 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; 484 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_];
430 } 485 }
431 DCHECK_EQ(range.length, 0U); 486 DCHECK_EQ(range.length, 0U);
432 } 487 }
433 488
434 // NOTE: If |aString| is empty, this intentionally replaces the 489 if (!NSEqualRanges(visualSelectionRange_, actualSelectionRange_)) {
435 // selection with empty. This seems consistent with the case where 490 [super replaceCharactersInRange:actualSelectionRange_ withString:aString];
436 // the input contained a mixture of characters and the string ended 491 } else {
437 // up not empty. 492 // NOTE: If |aString| is empty, this intentionally replaces the
438 [super insertText:aString]; 493 // selection with empty. This seems consistent with the case where
494 // the input contained a mixture of characters and the string ended
495 // up not empty.
496 [super insertText:aString];
497 }
439 } 498 }
440 499
441 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange 500 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
442 granularity:(NSSelectionGranularity)granularity { 501 granularity:(NSSelectionGranularity)granularity {
443 AutocompleteTextFieldObserver* observer = [self observer]; 502 AutocompleteTextFieldObserver* observer = [self observer];
444 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange 503 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange
445 granularity:granularity]; 504 granularity:granularity];
446 if (observer) 505 if (observer)
447 return observer->SelectionRangeForProposedRange(modifiedRange); 506 return observer->SelectionRangeForProposedRange(modifiedRange);
448 return modifiedRange; 507 return modifiedRange;
449 } 508 }
450 509
451 - (void)setSelectedRange:(NSRange)charRange 510 - (void)setSelectedRange:(NSRange)charRange
452 affinity:(NSSelectionAffinity)affinity 511 affinity:(NSSelectionAffinity)affinity
453 stillSelecting:(BOOL)flag { 512 stillSelecting:(BOOL)flag {
454 [super setSelectedRange:charRange affinity:affinity stillSelecting:flag]; 513 actualSelectionRange_ = charRange;
514 visualSelectionRange_ =
515 VisualSelectionRangeFromRange(charRange, [self string]);
516 [super setSelectedRange:visualSelectionRange_
517 affinity:affinity
518 stillSelecting:flag];
455 519
456 // We're only interested in selection changes directly caused by keyboard 520 // We're only interested in selection changes directly caused by keyboard
457 // input from the user. 521 // input from the user.
458 if (interpretingKeyEvents_) 522 if (interpretingKeyEvents_)
459 textChangedByKeyEvents_ = YES; 523 textChangedByKeyEvents_ = YES;
460 } 524 }
461 525
526 - (NSRange)selectedRange {
527 return actualSelectionRange_;
erikchen 2016/10/12 22:37:48 Is there any way we can avoid overriding this meth
lgrey 2016/10/13 15:42:51 We could expose a new method like |actualRange| or
erikchen 2016/10/13 17:08:28 If the only reason this code is necessary is to pr
lgrey 2016/10/13 21:03:56 Exposed actualSelectedRange_ as a property and use
528 }
529
462 - (void)interpretKeyEvents:(NSArray *)eventArray { 530 - (void)interpretKeyEvents:(NSArray *)eventArray {
463 DCHECK(!interpretingKeyEvents_); 531 DCHECK(!interpretingKeyEvents_);
464 interpretingKeyEvents_ = YES; 532 interpretingKeyEvents_ = YES;
465 textChangedByKeyEvents_ = NO; 533 textChangedByKeyEvents_ = NO;
466 AutocompleteTextFieldObserver* observer = [self observer]; 534 AutocompleteTextFieldObserver* observer = [self observer];
467 535
468 if (observer) 536 if (observer)
469 observer->OnBeforeChange(); 537 observer->OnBeforeChange();
470 538
471 [super interpretKeyEvents:eventArray]; 539 [super interpretKeyEvents:eventArray];
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
604 // ThemedWindowDrawing implementation. 672 // ThemedWindowDrawing implementation.
605 673
606 - (void)windowDidChangeTheme { 674 - (void)windowDidChangeTheme {
607 [self updateColorsToMatchTheme]; 675 [self updateColorsToMatchTheme];
608 } 676 }
609 677
610 - (void)windowDidChangeActive { 678 - (void)windowDidChangeActive {
611 } 679 }
612 680
613 @end 681 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698