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

TextEditingDelta Mac#29036

Merged
justinmc merged 34 commits intoflutter:mainfrom
justinmc:deltas-mac
Nov 18, 2021
Merged

TextEditingDelta Mac#29036
justinmc merged 34 commits intoflutter:mainfrom
justinmc:deltas-mac

Conversation

@justinmc
Copy link
Contributor

@justinmc justinmc commented Oct 5, 2021

This PR will bring TextEditingDeltas, originally introduced by @Renzo-Olivares, to Mac. This is very similar to the iOS implementation (#28418).

Partial fix for flutter/flutter#87972

@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlutterTextEditingDelta here is nearly identical to its iOS counterpart (file), but here we use Cocoa instead of UIKit, and Mac seems to be using ARC. Is there any worthwhile way to share the code between the two despite that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the cocoa import then? Is it possible to replace that (and the UIKit import in the iOS implementation) with Foundation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can implement something similar to TextInputModel in the shell/platform/common directory, that can be used across all platforms?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Cocoa import should have been AppKit. It looks like I can replace both with Foundation though 👍 . I'll attempt to create something reusable in shell/platform/common.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reusable class works, I've modified this PR to use it for both Mac and iOS. I'll use it in subsequent PRs on other platforms as well.

@justinmc justinmc marked this pull request as ready for review October 12, 2021 01:50
@justinmc justinmc changed the title (WIP) TextEditingDelta Mac TextEditingDelta Mac Oct 12, 2021
@property(nonatomic, nonnull) NSString* inputAction;

/**
* Whether to enable that the engine sends text input updates to the framework
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this wording is a bit awkward.

Maybe something like: Whether to enable the engine sending text input updates to the framework as TextEditingDeltas rather than one TextEditingValue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I modified that slightly:

 * Whether to enable the sending of text input updates from the engine to the
 * framework as TextEditingDeltas rather than as one TextEditingValue.

@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the cocoa import then? Is it possible to replace that (and the UIKit import in the iOS implementation) with Foundation?

@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can implement something similar to TextInputModel in the shell/platform/common directory, that can be used across all platforms?

// engine since it sent this update, and needs to now be made to match the
// engine's version of the state.
[self updateEditState];
if (!_enableDeltaModel) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not needed when the delta model is enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm this is Mac's weird "Ack". In the race condition case that the comment mentions, you're probably right that it needs to update the framework. I'll update it to send an "empty" delta in that case. The client can compare the oldText with their current text to detect the race condition.

if (_activeModel == nullptr) {
return;
}
NSString* textBeforeChange = [NSString stringWithUTF8String:_activeModel->GetText().c_str()];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cbracken is there a reason the UTF16 presentation of the string isn't exposed by TextInputModel?

Copy link
Member

@cbracken cbracken Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internally it stores everything in UTF-16, so there's no technical reason we couldn't. If I remember correctly, TextInputModel tries to expose only a UTF-8 representation but will gladly convert from UTF-16.

I suspect that I did that just to minimize API surface (and minimise potential for confusion/bugs) and because we didn't need it for Linux which is the first platform I wrote it for.

@justinmc
Copy link
Contributor Author

This PR suddenly had an error show up in it that didn't exist before when running the example app. Maybe due to a change in the framework? I'll get it running again and then address the comments above.


#ifndef FLUTTER_SHELL_PLATFORM_COMMON_TEXT_EDITING_DELTA_H_
#define FLUTTER_SHELL_PLATFORM_COMMON_TEXT_EDITING_DELTA_H_
#define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of codecvt_utf8_utf16 caused a deprecation warning on Windows that wanted me to use wstring, but I've silenced it since this code needs to work on multiple platforms.

Copy link
Member

@cbracken cbracken Nov 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep; we enable this in a few places. That said, instead of defining it here, please define this in shell/platform/common/BUILD.gn as we do here; that'll make it easier to rip this out later.

I've filed: flutter/flutter#93743

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that definition you linked exactly where I'd need to define this for this file? text_editing_delta.h and cc are included in that block in this PR. Or am I misunderstanding? I still seemed to get a failure in the Windows tests caused by this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah -- looks like that is indeed the exact target. Will try patching this in to see if I can repro.

@justinmc
Copy link
Contributor Author

Ready for re-review. I've updated the TextEditingDelta struct to store the strings as utf-16 while providing setters and constructors for both utf 8 and 16.

@skia-gold
Copy link

Gold has detected about 1 new digest(s) on patchset 28.
View them at https://flutter-engine-gold.skia.org/cl/github/29036

Copy link
Member

@cbracken cbracken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just reviewed the C++ bits -- haven't looked yet looked at the macOS specific Obj-C side.


namespace flutter {

struct TextEditingDelta {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add doc comments to this struct and the methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if I've missed any that usually should have doc comments. I skipped some things that seemed obvious.

Comment on lines +23 to +26
std::string oldText() const { return utf16ToUtf8(oldText_); }
std::string deltaText() const { return utf16ToUtf8(deltaText_); }
int deltaStart() const { return deltaStart_; }
int deltaEnd() const { return deltaEnd_; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, methods and functions should be named in UpperCamelCase().

Simple property getters/setters can be named foo_bar() and set_foo_bar().

Ref: https://google.github.io/styleguide/cppguide.html#Function_Names

public:
TextEditingDelta(std::u16string textBeforeChange,
TextRange range,
std::u16string text);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like TextEditingDeltas are copyable types. In this case, you should define:

  • A copy constructor: TextEditingDelta(const TextEditingDelta& other)
  • operator=: TextEditingDelta& operator=(const TextEditingDelta& other)

In this case, you should be able to declare them as default.

Ref: https://google.github.io/styleguide/cppguide.html#Copyable_Movable_Types

namespace flutter {

struct TextEditingDelta {
public:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not needed since struct members are public by default.


struct TextEditingDelta {
public:
TextEditingDelta(std::u16string textBeforeChange,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, parameters, fields, and variables should be formatted as text_before_change.

Ref: https://google.github.io/styleguide/cppguide.html#Variable_Names

int deltaEnd_;

void setDeltas(std::u16string oldText,
std::u16string newTxt,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do keep this method around, s/txt/text/ on this, plus switch to words_separated_by_underscores form for all of these.

deltaEnd_ = newEnd;
}

std::string utf16ToUtf8(std::u16string string) const {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here, use const std::u16string& text.

std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>
utf16_converter;
return utf16_converter.from_bytes(string);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these two functions don't reference any instance variables they should just be static functions in the C++; that said they're basically just two-liners, and I bet deleting these functions and just using wstring_convert inline in the two places it's used would actually be less lines of code 😉.

e.g. of where else we do this:

void TextInputModel::SetText(const std::string& text) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>
utf16_converter;
text_ = utf16_converter.from_bytes(text);
selection_ = TextRange(0);
composing_range_ = TextRange(0);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up keeping these methods around because I needed to use them in the initializers, where I think it would be harder to directly use wstring_convert. Let me know there is a slicker 1 liner way to use them that would be better.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope -- no one-liner trick here unfortunately. SGTM!

ASSERT_EQ(delta.oldText(), oldText);
ASSERT_EQ(delta.deltaText(), "");
ASSERT_EQ(delta.deltaStart(), -1);
ASSERT_EQ(delta.deltaEnd(), -1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and above: These could probably be EXPECT_EQ since these checks aren't interdependent on each other.

Ref: https://testing.googleblog.com/2008/07/tott-expect-vs-assert.html

// Here we are simulating inserting an "o" at the end of "hell".
std::string oldText = "hell";
std::string replacementText = "hello";
flutter::TextRange range = flutter::TextRange(0, 4);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flutter:: prefix isn't necessary here since you're already in the flutter namespace.

Just:

TextRange range(0, 4);

@justinmc
Copy link
Contributor Author

@cbracken Ready for re-review. Thanks for all of the suggestions and references as I figure out C++ at Google.

I think I took all of your suggestions except for two that I left questions on above.

@skia-gold
Copy link

Gold has detected about 253 new digest(s) on patchset 29.
View them at https://flutter-engine-gold.skia.org/cl/github/29036

TEST(TextEditingDeltaTest, TestTextEditingDeltaConstructor) {
// Here we are simulating inserting an "o" at the end of "hell".
std::string old_text = "hell";
std::string replacementText = "hello";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: replacement_text


TextEditingDelta::~TextEditingDelta() = default;

TextEditingDelta::TextEditingDelta(std::u16string text_before_change,
Copy link
Member

@cbracken cbracken Nov 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const std::u16string& text_before_change and a couple more places below.


TextEditingDelta(const std::string& text);

virtual ~TextEditingDelta();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could mark this default and kill off the one in the cc file.

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
Copy link
Member

@cbracken cbracken Nov 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this should be a blank line (kill the //)

Copy link
Member

@cbracken cbracken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on the C++ side modulo a couple minor nits. Will take a look at the macOS implementation after lunch!

@justinmc
Copy link
Contributor Author

Not sure why gold picked up 253 new gold images... I've merged in master and hopefully that fixes it.

: kTextAffinityDownstream;
}

- (NSRange)clampSelection:(NSRange*)range forText:(NSString*)text {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about clampRange:(NSRange*)range forText:(NSString*)text in case we ever use this on e.g. mark text?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it looks like I've removed all usage of this 🤷. I'll remove it altogether then.

Copy link
Member

@cbracken cbracken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@justinmc
Copy link
Contributor Author

@cbracken I'm not authorized to merge this PR apparently, can you do it?

@cbracken cbracken added waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. and removed waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. labels Nov 18, 2021
@cbracken
Copy link
Member

Ah -- the issue is that you're trying to merge against master. We switched the default branch to main last night.

@justinmc justinmc changed the base branch from master to main November 18, 2021 16:43
@justinmc
Copy link
Contributor Author

Ah there we go, thanks!

@justinmc justinmc merged commit 29c5631 into flutter:main Nov 18, 2021
@justinmc justinmc deleted the deltas-mac branch November 18, 2021 16:44
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Nov 19, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants