Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 217016e

Browse files
committed
addressing comments
1 parent 678480b commit 217016e

File tree

4 files changed

+73
-61
lines changed

4 files changed

+73
-61
lines changed

shell/platform/darwin/macos/framework/Source/FlutterPlatformNodeDelegateMacTest.mm

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,9 @@
280280
double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
281281

282282
double expectedFrameSize = rectSize * transformFactor / pixelRatio;
283-
EXPECT_EQ(
284-
NSEqualRects(native_text_field.frame, NSMakeRect(0, 0, expectedFrameSize, expectedFrameSize)),
285-
YES);
283+
EXPECT_EQ(NSEqualRects(native_text_field.frame, NSMakeRect(0, 600 - expectedFrameSize,
284+
expectedFrameSize, expectedFrameSize)),
285+
YES);
286286
// The text of TextInputPlugin only starts syncing editing state to the
287287
// native text field when it becomes the first responder.
288288
[native_text_field becomeFirstResponder];

shell/platform/darwin/macos/framework/Source/FlutterTextInputPluginTest.mm

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,7 @@ - (bool)testFirstRectForCharacterRange {
228228
FlutterTextPlatformNode textPlatformNode(&delegate, engine);
229229

230230
FlutterTextFieldMock* mockTextField =
231-
[[FlutterTextFieldMock alloc] initWithPlatformNode:&textPlatformNode];
232-
[mockTextField setFieldEditor:viewController.textInputPlugin];
231+
[[FlutterTextFieldMock alloc] initWithPlatformNode:&textPlatformNode andEngine:engine];
233232
[viewController.view addSubview:mockTextField];
234233
[mockTextField becomeFirstResponder];
235234

@@ -260,4 +259,39 @@ - (bool)testFirstRectForCharacterRange {
260259
EXPECT_EQ(NSEqualRanges(mockTextField.lastUpdatedSelection, NSMakeRange(1, 1)), YES);
261260
}
262261

262+
TEST(FlutterTextInputPluginTest, CanNotBecomeResponderIfNoViewController) {
263+
FlutterEngine* engine = CreateTestEngine();
264+
NSString* fixtures = @(testing::GetFixturesPath());
265+
FlutterDartProject* project = [[FlutterDartProject alloc]
266+
initWithAssetsPath:fixtures
267+
ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
268+
FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:project];
269+
[viewController loadView];
270+
[engine setViewController:viewController];
271+
// Creates a NSWindow so that the native text field can become first responder.
272+
NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
273+
styleMask:NSBorderlessWindowMask
274+
backing:NSBackingStoreBuffered
275+
defer:NO];
276+
window.contentView = viewController.view;
277+
278+
engine.semanticsEnabled = YES;
279+
280+
auto bridge = engine.accessibilityBridge.lock();
281+
FlutterPlatformNodeDelegateMac delegate(engine);
282+
ui::AXTree tree;
283+
ui::AXNode ax_node(&tree, nullptr, 0, 0);
284+
ui::AXNodeData node_data;
285+
node_data.SetValue("initial text");
286+
ax_node.SetData(node_data);
287+
delegate.Init(engine.accessibilityBridge, &ax_node);
288+
FlutterTextPlatformNode textPlatformNode(&delegate, engine);
289+
290+
FlutterTextField* textField = textPlatformNode.GetNativeViewAccessible();
291+
EXPECT_EQ([textField becomeFirstResponder], YES);
292+
// Removes view controller.
293+
[engine setViewController:nil];
294+
EXPECT_EQ([textField becomeFirstResponder], NO);
295+
}
296+
263297
} // namespace flutter::testing

shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.h

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,16 @@ class FlutterTextPlatformNode : public ui::AXPlatformNodeBase {
6969
* This causes VoiceOver to announce text editing feedbacks.
7070
*
7171
* All of the text editing events in this native text field are redirected to the
72-
* FlutterTextInputPlugin and will eventually update this text field through text input channel.
72+
* text input plugin returned by the FlutterTextFieldDelegate.
7373
*/
7474
@interface FlutterTextField : NSTextField
7575

7676
/**
77-
* Initializes a FlutterTextField.
78-
*
79-
* The plugin will be stored as an __unsafe_unretained pointer, and the FlutterTextField expects
80-
* the point is valid during its lifetime.
81-
*/
82-
- (instancetype)initWithPlatformNode:(flutter::FlutterTextPlatformNode*)node;
83-
84-
/**
85-
* Assign the field editor for this FlutterTextField. The plugin will be stored as an
86-
* __unsafe_unretained pointer, and the FlutterTextField expects the pointer is valid
87-
* during its lifetime.
77+
* Initializes a FlutterTextField that uses the textInputPlugin from the FlutterTextFieldDelegate
78+
* as its field editor.
8879
*/
89-
- (void)setFieldEditor:(FlutterTextInputPlugin*)plugin;
80+
- (instancetype)initWithPlatformNode:(flutter::FlutterTextPlatformNode*)node
81+
andEngine:(FlutterEngine*)engine;
9082

9183
/**
9284
* Updates the string value and the selection of this text field.

shell/platform/darwin/macos/framework/Source/FlutterTextInputSemanticsObject.mm

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,28 @@
1414

1515
#pragma mark - FlutterTextFieldCell
1616
/**
17-
* A convenient class that can be used to set a custom field editor for a
18-
* NSTextField.
19-
*
20-
* The FlutterTextField uses this class set the FlutterTextInputPlugin as
21-
* its field editor.
17+
* The NSCell for FlutterTextField that it sets the field editor of FlutterTextField
18+
* to the text input plugin from the FlutterTextFieldDelegate.
2219
*/
2320
@interface FlutterTextFieldCell : NSTextFieldCell
2421

2522
/**
2623
* Initializes the NSCell for the input NSTextField.
2724
*/
28-
- (instancetype)initWithTextField:(NSTextField*)textField;
29-
30-
/**
31-
* Assign the field editor to be used by the NSTextField.
32-
*/
33-
- (void)setFieldEditor:(FlutterTextInputPlugin*)editor;
25+
- (instancetype)initWithTextField:(FlutterTextField*)textField andEngine:(FlutterEngine*)engine;
3426

3527
@end
3628

3729
@implementation FlutterTextFieldCell {
38-
// NSTextView does not support _weak reference, so this class stores
39-
// __unsafe_unretained.
40-
__unsafe_unretained NSTextView* _editor;
30+
__weak FlutterEngine* _engine;
4131
}
4232

4333
#pragma mark - Private
4434

45-
- (instancetype)initWithTextField:(NSTextField*)textField {
35+
- (instancetype)initWithTextField:(FlutterTextField*)textField andEngine:(FlutterEngine*)engine {
4636
self = [super initTextCell:textField.stringValue];
4737
if (self) {
38+
_engine = engine;
4839
[self setControlView:textField];
4940
// Read only text fields are sent to the mac embedding as static
5041
// texts. This text field must be editable and selectable at this
@@ -55,14 +46,10 @@ - (instancetype)initWithTextField:(NSTextField*)textField {
5546
return self;
5647
}
5748

58-
- (void)setFieldEditor:(FlutterTextInputPlugin*)editor {
59-
_editor = editor;
60-
}
61-
6249
#pragma mark - NSCell
6350

6451
- (NSTextView*)fieldEditorForView:(NSView*)controlView {
65-
return _editor;
52+
return _engine.viewController.textInputPlugin;
6653
}
6754

6855
@end
@@ -71,38 +58,31 @@ - (NSTextView*)fieldEditorForView:(NSView*)controlView {
7158

7259
@implementation FlutterTextField {
7360
flutter::FlutterTextPlatformNode* _node;
74-
// NSTextView does not support _weak reference, so this class holds
75-
// __unsafe_unretained.
76-
__unsafe_unretained FlutterTextInputPlugin* _plugin;
61+
__weak FlutterEngine* _engine;
7762
}
7863

7964
#pragma mark - Public
8065

81-
- (instancetype)initWithPlatformNode:(flutter::FlutterTextPlatformNode*)node {
66+
- (instancetype)initWithPlatformNode:(flutter::FlutterTextPlatformNode*)node
67+
andEngine:(FlutterEngine*)engine {
8268
self = [super initWithFrame:NSZeroRect];
8369
if (self) {
8470
_node = node;
85-
[self setCell:[[FlutterTextFieldCell alloc] initWithTextField:self]];
71+
_engine = engine;
72+
[self setCell:[[FlutterTextFieldCell alloc] initWithTextField:self andEngine:engine]];
8673
}
8774
return self;
8875
}
8976

90-
- (void)setFieldEditor:(FlutterTextInputPlugin*)plugin {
91-
_plugin = plugin;
92-
NSAssert([self.cell isKindOfClass:[FlutterTextFieldCell class]],
93-
@"The cell of a FlutterTextField must be a FlutterTextFieldCell");
94-
FlutterTextFieldCell* cell = (FlutterTextFieldCell*)self.cell;
95-
[cell setFieldEditor:plugin];
96-
}
97-
9877
- (void)updateString:(NSString*)string withSelection:(NSRange)selection {
99-
NSAssert(_plugin.client == self,
78+
FlutterTextInputPlugin* plugin = _engine.viewController.textInputPlugin;
79+
NSAssert(plugin.client == self,
10080
@"Can't update FlutterTextField when it is not the first responder");
10181
if (![[self stringValue] isEqualToString:string]) {
10282
[self setStringValue:string];
10383
}
104-
if (!NSEqualRanges(_plugin.selectedRange, selection)) {
105-
[_plugin setSelectedRange:selection];
84+
if (!NSEqualRanges(plugin.selectedRange, selection)) {
85+
[plugin setSelectedRange:selection];
10686
}
10787
}
10888

@@ -124,13 +104,17 @@ - (void)setAccessibilityFocused:(BOOL)isFocused {
124104
#pragma mark - NSResponder
125105

126106
- (BOOL)becomeFirstResponder {
127-
if (_plugin.client == self && [_plugin isFirstResponder]) {
107+
FlutterTextInputPlugin* plugin = _engine.viewController.textInputPlugin;
108+
if (!plugin) {
109+
return NO;
110+
}
111+
if (plugin.client == self && [plugin isFirstResponder]) {
128112
// This text field is already the first responder.
129113
return YES;
130114
}
131115
BOOL result = [super becomeFirstResponder];
132116
if (result) {
133-
_plugin.client = self;
117+
plugin.client = self;
134118
// The default implementation of the becomeFirstResponder will change the
135119
// text editing state. Need to manually set it back.
136120
NSString* textValue = @(_node->GetStringAttribute(ax::mojom::StringAttribute::kValue).data());
@@ -155,16 +139,14 @@ - (BOOL)becomeFirstResponder {
155139
FlutterEngine* engine) {
156140
Init(delegate);
157141
engine_ = engine;
158-
native_text_field_ = [[FlutterTextField alloc] initWithPlatformNode:this];
159-
[native_text_field_ setFieldEditor:engine.viewController.textInputPlugin];
142+
native_text_field_ = [[FlutterTextField alloc] initWithPlatformNode:this andEngine:engine];
160143
native_text_field_.bezeled = NO;
161144
native_text_field_.drawsBackground = NO;
162145
native_text_field_.bordered = NO;
163146
native_text_field_.focusRingType = NSFocusRingTypeNone;
164147
}
165148

166149
FlutterTextPlatformNode::~FlutterTextPlatformNode() {
167-
[native_text_field_ setFieldEditor:nil];
168150
EnsureDetachedFromView();
169151
}
170152

@@ -176,6 +158,9 @@ - (BOOL)becomeFirstResponder {
176158
}
177159

178160
NSRect FlutterTextPlatformNode::GetFrameRelativeToControllerView() {
161+
if (!engine_.viewController.viewLoaded) {
162+
return NSZeroRect;
163+
}
179164
FlutterPlatformNodeDelegate* delegate = static_cast<FlutterPlatformNodeDelegate*>(GetDelegate());
180165
bool offscreen;
181166
auto bridge_ptr = delegate->GetOwnerBridge().lock();
@@ -188,8 +173,9 @@ - (BOOL)becomeFirstResponder {
188173
// increasing to bottom-right. Flip the y coordinate to convert from Flutter
189174
// coordinates to macOS coordinates.
190175
ns_local_bounds.origin.y = -ns_local_bounds.origin.y - ns_local_bounds.size.height;
191-
return [engine_.viewController.flutterView convertRectFromBacking:ns_local_bounds];
192-
;
176+
NSRect ns_view_bounds =
177+
[engine_.viewController.flutterView convertRectFromBacking:ns_local_bounds];
178+
return [engine_.viewController.flutterView convertRect:ns_view_bounds toView:nil];
193179
}
194180

195181
bool FlutterTextPlatformNode::EnsureAttachedToView() {

0 commit comments

Comments
 (0)