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

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

Issue 1531213002: Mac: Implement firstRectForCharacterRange:actualRange in BridgedContentView. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed review comments. Created 4 years, 11 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 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_native_widget.h" 5 #import "ui/views/cocoa/bridged_native_widget.h"
6 6
7 #import <Cocoa/Cocoa.h> 7 #import <Cocoa/Cocoa.h>
8 8
9 #import "base/mac/foundation_util.h" 9 #import "base/mac/foundation_util.h"
10 #import "base/mac/mac_util.h" 10 #import "base/mac/mac_util.h"
11 #import "base/mac/sdk_forward_declarations.h" 11 #import "base/mac/sdk_forward_declarations.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h" 13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/sys_string_conversions.h" 14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #import "testing/gtest_mac.h" 16 #import "testing/gtest_mac.h"
17 #import "ui/base/cocoa/window_size_constants.h" 17 #import "ui/base/cocoa/window_size_constants.h"
18 #include "ui/base/ime/input_method.h" 18 #include "ui/base/ime/input_method.h"
19 #import "ui/gfx/test/ui_cocoa_test_helper.h" 19 #import "ui/gfx/test/ui_cocoa_test_helper.h"
20 #import "ui/gfx/mac/coordinate_conversion.h"
20 #import "ui/views/cocoa/bridged_content_view.h" 21 #import "ui/views/cocoa/bridged_content_view.h"
21 #import "ui/views/cocoa/native_widget_mac_nswindow.h" 22 #import "ui/views/cocoa/native_widget_mac_nswindow.h"
22 #import "ui/views/cocoa/views_nswindow_delegate.h" 23 #import "ui/views/cocoa/views_nswindow_delegate.h"
23 #include "ui/views/controls/textfield/textfield.h" 24 #include "ui/views/controls/textfield/textfield.h"
24 #include "ui/views/view.h" 25 #include "ui/views/view.h"
25 #include "ui/views/widget/native_widget_mac.h" 26 #include "ui/views/widget/native_widget_mac.h"
26 #include "ui/views/widget/root_view.h" 27 #include "ui/views/widget/root_view.h"
27 #include "ui/views/widget/widget.h" 28 #include "ui/views/widget/widget.h"
28 #include "ui/views/widget/widget_observer.h" 29 #include "ui/views/widget/widget_observer.h"
29 30
30 using base::ASCIIToUTF16; 31 using base::ASCIIToUTF16;
31 using base::SysNSStringToUTF8; 32 using base::SysNSStringToUTF8;
32 using base::SysNSStringToUTF16; 33 using base::SysNSStringToUTF16;
33 using base::SysUTF8ToNSString; 34 using base::SysUTF8ToNSString;
34 35
35 #define EXPECT_EQ_RANGE(a, b) \ 36 #define EXPECT_EQ_RANGE(a, b) \
36 EXPECT_EQ(a.location, b.location); \ 37 EXPECT_EQ(a.location, b.location); \
37 EXPECT_EQ(a.length, b.length); 38 EXPECT_EQ(a.length, b.length);
38 39
39 namespace { 40 namespace {
40 41
41 // Empty range shortcut for readibility. 42 // Empty range shortcut for readibility.
42 NSRange EmptyRange() { 43 NSRange EmptyRange() {
43 return NSMakeRange(NSNotFound, 0); 44 return NSMakeRange(NSNotFound, 0);
44 } 45 }
45 46
47 // Sets |composition_text| as the composition text with caret placed at
48 // |caret_pos| and updates |caret_range|.
49 void SetCompositionText(ui::TextInputClient* client,
50 const base::string16& composition_text,
51 const int caret_pos,
52 NSRange* caret_range) {
53 ui::CompositionText composition;
54 composition.selection = gfx::Range(caret_pos);
55 composition.text = composition_text;
56 client->SetCompositionText(composition);
57 if (caret_range)
58 *caret_range = NSMakeRange(caret_pos, 0);
59 }
60
61 // Returns a zero width rectangle corresponding to current caret position.
62 gfx::Rect GetCaretBounds(const ui::TextInputClient* client) {
63 gfx::Rect caret_bounds = client->GetCaretBounds();
64 caret_bounds.set_width(0);
65 return caret_bounds;
66 }
67
68 // Returns a zero width rectangle corresponding to caret bounds when it's placed
69 // at |caret_pos| and updates |caret_range|.
70 gfx::Rect GetCaretBoundsForPosition(ui::TextInputClient* client,
71 const base::string16& composition_text,
72 const int caret_pos,
73 NSRange* caret_range) {
74 SetCompositionText(client, composition_text, caret_pos, caret_range);
75 return GetCaretBounds(client);
76 }
77
78 // Returns the expected boundary rectangle for characters of |composition_text|
79 // within the |query_range|.
80 gfx::Rect GetExpectedBoundsForRange(ui::TextInputClient* client,
81 const base::string16& composition_text,
82 NSRange query_range) {
83 gfx::Rect left_caret = GetCaretBoundsForPosition(client, composition_text,
84 query_range.location, nil);
85 gfx::Rect right_caret = GetCaretBoundsForPosition(
86 client, composition_text, query_range.location + query_range.length, nil);
87
88 // The expected bounds correspond to the area between the left and right caret
89 // positions.
90 return gfx::Rect(left_caret.x(), left_caret.y(),
91 right_caret.x() - left_caret.x(), left_caret.height());
92 }
93
46 } // namespace 94 } // namespace
47 95
48 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates 96 // Class to override -[NSWindow toggleFullScreen:] to a no-op. This simulates
49 // NSWindow's behavior when attempting to toggle fullscreen state again, when 97 // NSWindow's behavior when attempting to toggle fullscreen state again, when
50 // the last attempt failed but Cocoa has not yet sent 98 // the last attempt failed but Cocoa has not yet sent
51 // windowDidFailToEnterFullScreen:. 99 // windowDidFailToEnterFullScreen:.
52 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow { 100 @interface BridgedNativeWidgetTestFullScreenWindow : NativeWidgetMacNSWindow {
53 @private 101 @private
54 int ignoredToggleFullScreenCount_; 102 int ignoredToggleFullScreenCount_;
55 } 103 }
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
121 // before the tests covering the Init() flow are ready to do that. 169 // before the tests covering the Init() flow are ready to do that.
122 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS; 170 init_params_.type = Widget::InitParams::TYPE_WINDOW_FRAMELESS;
123 171
124 // To control the lifetime without an actual window that must be closed, 172 // To control the lifetime without an actual window that must be closed,
125 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET. 173 // tests in this file need to use WIDGET_OWNS_NATIVE_WIDGET.
126 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 174 init_params_.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
127 175
128 // Opacity defaults to "infer" which is usually updated by ViewsDelegate. 176 // Opacity defaults to "infer" which is usually updated by ViewsDelegate.
129 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW; 177 init_params_.opacity = Widget::InitParams::OPAQUE_WINDOW;
130 178
179 init_params_.bounds = gfx::Rect(100, 100, 100, 100);
180
131 native_widget_mac_->GetWidget()->Init(init_params_); 181 native_widget_mac_->GetWidget()->Init(init_params_);
132 } 182 }
133 183
134 protected: 184 protected:
135 scoped_ptr<Widget> widget_; 185 scoped_ptr<Widget> widget_;
136 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|. 186 MockNativeWidgetMac* native_widget_mac_; // Weak. Owned by |widget_|.
137 187
138 // Make the InitParams available to tests to cover initialization codepaths. 188 // Make the InitParams available to tests to cover initialization codepaths.
139 Widget::InitParams init_params_; 189 Widget::InitParams init_params_;
140 }; 190 };
(...skipping 26 matching lines...) Expand all
167 217
168 BridgedNativeWidgetTest::BridgedNativeWidgetTest() { 218 BridgedNativeWidgetTest::BridgedNativeWidgetTest() {
169 } 219 }
170 220
171 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() { 221 BridgedNativeWidgetTest::~BridgedNativeWidgetTest() {
172 } 222 }
173 223
174 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) { 224 void BridgedNativeWidgetTest::InstallTextField(const std::string& text) {
175 Textfield* textfield = new Textfield(); 225 Textfield* textfield = new Textfield();
176 textfield->SetText(ASCIIToUTF16(text)); 226 textfield->SetText(ASCIIToUTF16(text));
227 textfield->SetBoundsRect(init_params_.bounds);
177 view_->AddChildView(textfield); 228 view_->AddChildView(textfield);
178 229
179 // Request focus so the InputMethod can dispatch events to the RootView, and 230 // Request focus so the InputMethod can dispatch events to the RootView, and
180 // have them delivered to the textfield. Note that focusing a textfield 231 // have them delivered to the textfield. Note that focusing a textfield
181 // schedules a task to flash the cursor, so this requires |message_loop_|. 232 // schedules a task to flash the cursor, so this requires |message_loop_|.
182 textfield->RequestFocus(); 233 textfield->RequestFocus();
183 234
184 [ns_view_ setTextInputClient:textfield]; 235 [ns_view_ setTextInputClient:textfield];
185 } 236 }
186 237
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 EXPECT_EQ("a", GetText()); 549 EXPECT_EQ("a", GetText());
499 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]); 550 EXPECT_EQ_RANGE(NSMakeRange(1, 0), [ns_view_ selectedRange]);
500 551
501 // Should succeed after moving left first. 552 // Should succeed after moving left first.
502 [ns_view_ doCommandBySelector:@selector(moveLeft:)]; 553 [ns_view_ doCommandBySelector:@selector(moveLeft:)];
503 [ns_view_ doCommandBySelector:@selector(deleteForward:)]; 554 [ns_view_ doCommandBySelector:@selector(deleteForward:)];
504 EXPECT_EQ("", GetText()); 555 EXPECT_EQ("", GetText());
505 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]); 556 EXPECT_EQ_RANGE(NSMakeRange(0, 0), [ns_view_ selectedRange]);
506 } 557 }
507 558
559 // Test firstRectForCharacterRange:actualRange for cases where query range is
560 // empty or outside composition range.
561 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange_Caret) {
562 InstallTextField("");
563 ui::TextInputClient* client = [ns_view_ textInputClient];
564
565 // No composition. Ensure bounds and range corresponding to the current caret
566 // position are returned.
567 // Initially selection range will be [0, 0].
568 NSRange caret_range = NSMakeRange(0, 0);
569 NSRange query_range = NSMakeRange(1, 1);
570 NSRange actual_range;
571 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
572 actualRange:&actual_range];
573 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
574 EXPECT_EQ_RANGE(caret_range, actual_range);
575
576 // Set composition with caret before second character ('e').
577 const base::string16 kTestString = base::ASCIIToUTF16("test_str");
578 const size_t kTextLength = 8;
579 SetCompositionText(client, kTestString, 1, &caret_range);
580
581 // Test bounds returned for empty range before second character ('e') are same
582 // as caret bounds when placed before second character.
583 query_range = NSMakeRange(1, 0);
584 rect = [ns_view_ firstRectForCharacterRange:query_range
585 actualRange:&actual_range];
586 EXPECT_EQ(GetCaretBoundsForPosition(client, kTestString, 1, &caret_range),
587 gfx::ScreenRectFromNSRect(rect));
588 EXPECT_EQ_RANGE(query_range, actual_range);
589
590 // Test bounds returned for empty range after the composition text are same as
591 // caret bounds when placed after the composition text.
592 query_range = NSMakeRange(kTextLength, 0);
593 rect = [ns_view_ firstRectForCharacterRange:query_range
594 actualRange:&actual_range];
595 EXPECT_NE(GetCaretBoundsForPosition(client, kTestString, 1, &caret_range),
596 gfx::ScreenRectFromNSRect(rect));
597 EXPECT_EQ(
598 GetCaretBoundsForPosition(client, kTestString, kTextLength, &caret_range),
599 gfx::ScreenRectFromNSRect(rect));
600 EXPECT_EQ_RANGE(query_range, actual_range);
601
602 // Query outside composition range. Ensure bounds and range corresponding to
603 // the current caret position are returned.
604 query_range = NSMakeRange(kTextLength + 1, 0);
605 rect = [ns_view_ firstRectForCharacterRange:query_range
606 actualRange:&actual_range];
607 EXPECT_EQ(GetCaretBounds(client), gfx::ScreenRectFromNSRect(rect));
608 EXPECT_EQ_RANGE(caret_range, actual_range);
609
610 // Make sure not crashing by passing null pointer instead of actualRange.
611 rect = [ns_view_ firstRectForCharacterRange:query_range actualRange:nullptr];
612 }
613
614 // Test firstRectForCharacterRange:actualRange for non-empty query ranges within
615 // the composition range.
616 TEST_F(BridgedNativeWidgetTest, TextInput_FirstRectForCharacterRange) {
617 InstallTextField("");
618 ui::TextInputClient* client = [ns_view_ textInputClient];
619
620 // Set composition text.
tapted 2016/01/04 02:28:10 nit: this comment doesn't add much
karandeepb 2016/01/04 04:33:56 Done.
621 const base::string16 kTestString = base::ASCIIToUTF16("test_str");
622 const size_t kTextLength = 8;
623 SetCompositionText(client, kTestString, 1, nil);
tapted 2016/01/04 02:28:10 so here, `nullptr` is slightly more correct than `
karandeepb 2016/01/04 04:33:56 Done.
624
625 // Query bounds for the whole composition string.
626 NSRange query_range = NSMakeRange(0, kTextLength);
627 NSRange actual_range;
628 NSRect rect = [ns_view_ firstRectForCharacterRange:query_range
629 actualRange:&actual_range];
630 EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range),
631 gfx::ScreenRectFromNSRect(rect));
632 EXPECT_EQ_RANGE(query_range, actual_range);
633
634 // Query bounds for the substring "est_".
635 query_range = NSMakeRange(1, 4);
636 rect = [ns_view_ firstRectForCharacterRange:query_range
637 actualRange:&actual_range];
638 EXPECT_EQ(GetExpectedBoundsForRange(client, kTestString, query_range),
639 gfx::ScreenRectFromNSRect(rect));
640 EXPECT_EQ_RANGE(query_range, actual_range);
641 }
642
508 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest; 643 typedef BridgedNativeWidgetTestBase BridgedNativeWidgetSimulateFullscreenTest;
509 644
510 // Simulate the notifications that AppKit would send out if a fullscreen 645 // Simulate the notifications that AppKit would send out if a fullscreen
511 // operation begins, and then fails and must abort. This notification sequence 646 // operation begins, and then fails and must abort. This notification sequence
512 // was determined by posting delayed tasks to toggle fullscreen state and then 647 // was determined by posting delayed tasks to toggle fullscreen state and then
513 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause 648 // mashing Ctrl+Left/Right to keep OSX in a transition between Spaces to cause
514 // the fullscreen transition to fail. 649 // the fullscreen transition to fail.
515 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { 650 TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) {
516 if (base::mac::IsOSSnowLeopard()) 651 if (base::mac::IsOSSnowLeopard())
517 return; 652 return;
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
576 [center postNotificationName:NSWindowDidExitFullScreenNotification 711 [center postNotificationName:NSWindowDidExitFullScreenNotification
577 object:window]; 712 object:window];
578 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change. 713 EXPECT_EQ(1, [window ignoredToggleFullScreenCount]); // No change.
579 EXPECT_FALSE(bridge()->target_fullscreen_state()); 714 EXPECT_FALSE(bridge()->target_fullscreen_state());
580 715
581 widget_->CloseNow(); 716 widget_->CloseNow();
582 } 717 }
583 718
584 } // namespace test 719 } // namespace test
585 } // namespace views 720 } // namespace views
OLDNEW
« ui/views/cocoa/bridged_content_view.mm ('K') | « ui/views/cocoa/bridged_content_view.mm ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698