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

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: Expose actualSelectionRange as a property change calls to selectedRange to use it instead when appr… 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 VisualSelectedRangeFromRange(NSRange range, NSString* string) {
54 if (range.location >= string.length) {
Alexei Svitkine (slow) 2016/10/13 21:19:43 Nit: No {}'s
lgrey 2016/10/14 14:20:55 Done.
55 return range;
56 }
57 base::string16 text = base::SysNSStringToUTF16(string);
58 base::i18n::BreakIterator grapheme_iterator(
59 text, base::i18n::BreakIterator::BREAK_CHARACTER);
60 if (!grapheme_iterator.Init()) {
Alexei Svitkine (slow) 2016/10/13 21:19:43 Nit: No {}'s
lgrey 2016/10/14 14:20:55 Done.
61 return range;
62 }
63 // This works because NSString uses UTF-16 code units.
64 while (range.location < text.length() &&
65 !grapheme_iterator.IsGraphemeBoundary(
66 static_cast<size_t>(range.location))) {
67 range.location++;
68 if (range.length > 0) {
Alexei Svitkine (slow) 2016/10/13 21:19:43 Nit: No {}'s
lgrey 2016/10/14 14:20:55 Done.
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
106 @synthesize actualSelectedRange = actualSelectedRange_;
107
59 - (BOOL)shouldDrawInsertionPoint { 108 - (BOOL)shouldDrawInsertionPoint {
60 return [super shouldDrawInsertionPoint] && 109 return [super shouldDrawInsertionPoint] &&
61 ![[[self delegate] cell] hideFocusState]; 110 ![[[self delegate] cell] hideFocusState];
62 } 111 }
63 112
64 - (id)initWithFrame:(NSRect)frameRect { 113 - (id)initWithFrame:(NSRect)frameRect {
65 if ((self = [super initWithFrame:frameRect])) { 114 if ((self = [super initWithFrame:frameRect])) {
66 dropHandler_.reset([[URLDropTargetHandler alloc] initWithView:self]); 115 dropHandler_.reset([[URLDropTargetHandler alloc] initWithView:self]);
67 116
68 forbiddenCharacters_.reset([[NSCharacterSet controlCharacterSet] retain]); 117 forbiddenCharacters_.reset([[NSCharacterSet controlCharacterSet] retain]);
(...skipping 324 matching lines...) Expand 10 before | Expand all | Expand 10 after
393 // (URLDropTarget protocol) 442 // (URLDropTarget protocol)
394 - (void)draggingExited:(id<NSDraggingInfo>)sender { 443 - (void)draggingExited:(id<NSDraggingInfo>)sender {
395 return [dropHandler_ draggingExited:sender]; 444 return [dropHandler_ draggingExited:sender];
396 } 445 }
397 446
398 // (URLDropTarget protocol) 447 // (URLDropTarget protocol)
399 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { 448 - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
400 return [dropHandler_ performDragOperation:sender]; 449 return [dropHandler_ performDragOperation:sender];
401 } 450 }
402 451
452 - (void)_userReplaceRange:(NSRange)range withString:(NSString*)string {
453 if (NSEqualRanges(visualSelectedRange_, range) &&
454 !NSEqualRanges(visualSelectedRange_, actualSelectedRange_)) {
455 range = actualSelectedRange_;
456 }
457 [super _userReplaceRange:range withString:string];
458 }
459
403 // Prevent control characters from being entered into the Omnibox. 460 // Prevent control characters from being entered into the Omnibox.
404 // This is invoked for keyboard entry, not for pasting. 461 // This is invoked for keyboard entry, not for pasting.
405 - (void)insertText:(id)aString { 462 - (void)insertText:(id)aString {
406 AutocompleteTextFieldObserver* observer = [self observer]; 463 AutocompleteTextFieldObserver* observer = [self observer];
407 if (observer) 464 if (observer)
408 observer->OnInsertText(); 465 observer->OnInsertText();
409 466
410 // Repeatedly remove control characters. The loop will only ever 467 // Repeatedly remove control characters. The loop will only ever
411 // execute at all when the user enters control characters (using 468 // execute at all when the user enters control characters (using
412 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would 469 // Ctrl-Alt- or Ctrl-Q). Making this generally efficient would
(...skipping 11 matching lines...) Expand all
424 } else { 481 } else {
425 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; 482 NSRange range = [aString rangeOfCharacterFromSet:forbiddenCharacters_];
426 while (range.location != NSNotFound) { 483 while (range.location != NSNotFound) {
427 aString = 484 aString =
428 [aString stringByReplacingCharactersInRange:range withString:@""]; 485 [aString stringByReplacingCharactersInRange:range withString:@""];
429 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_]; 486 range = [aString rangeOfCharacterFromSet:forbiddenCharacters_];
430 } 487 }
431 DCHECK_EQ(range.length, 0U); 488 DCHECK_EQ(range.length, 0U);
432 } 489 }
433 490
434 // NOTE: If |aString| is empty, this intentionally replaces the 491 if (!NSEqualRanges(visualSelectedRange_, actualSelectedRange_)) {
435 // selection with empty. This seems consistent with the case where 492 [super replaceCharactersInRange:actualSelectedRange_ withString:aString];
436 // the input contained a mixture of characters and the string ended 493 } else {
437 // up not empty. 494 // NOTE: If |aString| is empty, this intentionally replaces the
438 [super insertText:aString]; 495 // selection with empty. This seems consistent with the case where
496 // the input contained a mixture of characters and the string ended
497 // up not empty.
498 [super insertText:aString];
499 }
439 } 500 }
440 501
441 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange 502 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
442 granularity:(NSSelectionGranularity)granularity { 503 granularity:(NSSelectionGranularity)granularity {
443 AutocompleteTextFieldObserver* observer = [self observer]; 504 AutocompleteTextFieldObserver* observer = [self observer];
444 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange 505 NSRange modifiedRange = [super selectionRangeForProposedRange:proposedSelRange
445 granularity:granularity]; 506 granularity:granularity];
446 if (observer) 507 if (observer)
447 return observer->SelectionRangeForProposedRange(modifiedRange); 508 return observer->SelectionRangeForProposedRange(modifiedRange);
448 return modifiedRange; 509 return modifiedRange;
449 } 510 }
450 511
451 - (void)setSelectedRange:(NSRange)charRange 512 - (void)setSelectedRange:(NSRange)charRange
452 affinity:(NSSelectionAffinity)affinity 513 affinity:(NSSelectionAffinity)affinity
453 stillSelecting:(BOOL)flag { 514 stillSelecting:(BOOL)flag {
454 [super setSelectedRange:charRange affinity:affinity stillSelecting:flag]; 515 actualSelectedRange_ = charRange;
516 visualSelectedRange_ = VisualSelectedRangeFromRange(charRange, [self string]);
517 [super setSelectedRange:visualSelectedRange_
518 affinity:affinity
519 stillSelecting:flag];
455 520
456 // We're only interested in selection changes directly caused by keyboard 521 // We're only interested in selection changes directly caused by keyboard
457 // input from the user. 522 // input from the user.
458 if (interpretingKeyEvents_) 523 if (interpretingKeyEvents_)
459 textChangedByKeyEvents_ = YES; 524 textChangedByKeyEvents_ = YES;
460 } 525 }
461 526
462 - (void)interpretKeyEvents:(NSArray *)eventArray { 527 - (void)interpretKeyEvents:(NSArray *)eventArray {
463 DCHECK(!interpretingKeyEvents_); 528 DCHECK(!interpretingKeyEvents_);
464 interpretingKeyEvents_ = YES; 529 interpretingKeyEvents_ = YES;
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
557 [textStorage setAttributedString:aString]; 622 [textStorage setAttributedString:aString];
558 623
559 // The text has been changed programmatically. The observer should know 624 // The text has been changed programmatically. The observer should know
560 // this change, so setting |textChangedByKeyEvents_| to NO to 625 // this change, so setting |textChangedByKeyEvents_| to NO to
561 // prevent its OnDidChange() method from being called unnecessarily. 626 // prevent its OnDidChange() method from being called unnecessarily.
562 textChangedByKeyEvents_ = NO; 627 textChangedByKeyEvents_ = NO;
563 } 628 }
564 629
565 - (BOOL)validateMenuItem:(NSMenuItem*)item { 630 - (BOOL)validateMenuItem:(NSMenuItem*)item {
566 if ([item action] == @selector(copyToFindPboard:)) 631 if ([item action] == @selector(copyToFindPboard:))
567 return [self selectedRange].length > 0; 632 return actualSelectedRange_.length > 0;
568 if ([item action] == @selector(pasteAndGo:)) { 633 if ([item action] == @selector(pasteAndGo:)) {
569 // TODO(rohitrao): If the clipboard is empty, should we show a 634 // TODO(rohitrao): If the clipboard is empty, should we show a
570 // greyed-out "Paste and Go" or nothing at all? 635 // greyed-out "Paste and Go" or nothing at all?
571 AutocompleteTextFieldObserver* observer = [self observer]; 636 AutocompleteTextFieldObserver* observer = [self observer];
572 DCHECK(observer); 637 DCHECK(observer);
573 return observer->CanPasteAndGo(); 638 return observer->CanPasteAndGo();
574 } 639 }
575 return [super validateMenuItem:item]; 640 return [super validateMenuItem:item];
576 } 641 }
577 642
578 - (void)copyToFindPboard:(id)sender { 643 - (void)copyToFindPboard:(id)sender {
579 NSRange selectedRange = [self selectedRange]; 644 if (actualSelectedRange_.length == 0)
580 if (selectedRange.length == 0)
581 return; 645 return;
582 NSAttributedString* selection = 646 NSAttributedString* selection =
583 [self attributedSubstringForProposedRange:selectedRange 647 [self attributedSubstringForProposedRange:actualSelectedRange_
584 actualRange:NULL]; 648 actualRange:NULL];
585 if (!selection) 649 if (!selection)
586 return; 650 return;
587 651
588 [[FindPasteboard sharedInstance] setFindText:[selection string]]; 652 [[FindPasteboard sharedInstance] setFindText:[selection string]];
589 } 653 }
590 654
591 - (void)drawRect:(NSRect)rect { 655 - (void)drawRect:(NSRect)rect {
592 [super drawRect:rect]; 656 [super drawRect:rect];
593 autocomplete_text_field::DrawGrayTextAutocompletion( 657 autocomplete_text_field::DrawGrayTextAutocompletion(
(...skipping 10 matching lines...) Expand all
604 // ThemedWindowDrawing implementation. 668 // ThemedWindowDrawing implementation.
605 669
606 - (void)windowDidChangeTheme { 670 - (void)windowDidChangeTheme {
607 [self updateColorsToMatchTheme]; 671 [self updateColorsToMatchTheme];
608 } 672 }
609 673
610 - (void)windowDidChangeActive { 674 - (void)windowDidChangeActive {
611 } 675 }
612 676
613 @end 677 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698