Skip to content

[linux] Implements basic ASCII text input #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
May 3, 2018

Conversation

awdavies
Copy link
Contributor

Text input supports:
-- Deleting highlighted sections of text (highlight must be done w/ the mouse pointer for now).
-- Writing basic ASCII
-- Moving the cursor left and right.
-- Home/End keys to go to the beginning/end of a line.

This change also adds some header guard cleanup since paths have changed.

@awdavies awdavies changed the title Implements basic ASCII text input [linux] Implements basic ASCII text input Apr 19, 2018
@awdavies awdavies requested a review from stuartmorgan-g April 19, 2018 20:53
Copy link
Collaborator

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

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

Nice!

#include <string>

namespace flutter_desktop_embedding {

typedef std::function<void(const std::string &, const Json::Value &)>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you comment this? E.g., meanings of the arguments.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@@ -31,14 +36,24 @@ class Plugin {
// |input_blocking| Determines whether user input should be blocked during the
// duration of this plugin's platform callback handler (in most cases this
// can be set to false).
explicit Plugin(std::string channel, bool input_blocking = false)
: channel_(channel), input_blocking_(input_blocking) {}
explicit Plugin(std::string channel, PlatformCallback platform_callback,
Copy link
Collaborator

Choose a reason for hiding this comment

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

For now (things may change in the API revamp), how about eliminating platform_callback and having the logic for building and sending a response message given the channel and JSON be part of the Plugin base class?

Copy link
Contributor Author

@awdavies awdavies Apr 24, 2018

Choose a reason for hiding this comment

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

I'm confused about what this means. The plugin can't make a call to the engine unless it has an instance of it. Should we then be including a field with a pointer to the flutter engine in the base class?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, either way it's tied to the engine; I think making it explicit with the engine pointer is reasonable since a plugin is fundamentally a channel for talking to the engine.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated this to take a setter method. Unsure if this is what you had in mind.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The client is still responsible providing the callback, not just the engine as discussed above. Summarizing from the offline discussion earlier, my take on this is:

  • The callback logic is always going to be the same modulo the engine pointer.
  • That logic is fundamentally about the details of the message format used by the engine.
  • The core purpose of the Plugin class is to provide a higher-level abstraction around communicating with the engine.
    Given that, a protected method within Plugin seems like the right place for that logic to live, rather than requiring something outside of Plugin to implement it and provide that implementation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ping on this. I accidentally replied as a separate comment rather than as part of the review, so I'm guessing it was lost.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alright, got it in. This required a minor change to the file chooser plugin, btw (just need it to call the engine callback instead of returning the info as part of the HandlePlatformMessage response).

@@ -52,10 +57,20 @@ class PluginHandler {
std::function<void(void)> input_block_cb = [] {},
std::function<void(void)> input_unblock_cb = [] {});

const std::vector<KeyboardHookFunction>& keyboard_hooks() const {
Copy link
Collaborator

Choose a reason for hiding this comment

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

These need declaration comments explaining what they are.

But as with the comments on Plugin, ideally we should move these out of the core class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed! 👍

int client_id = client_id_json.asInt();
if (input_models_.find(client_id) == input_models_.end()) {
input_models_.insert(std::make_pair(
client_id, std::make_unique<TextInputModel>(client_id)));
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we cap the size of this? Wasn't there some discussion of the ID always going up over time even for the same set of text fields?

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 think we should, yes. However inflating text ID fields should be fixed once we send back valid callback responses via HandlePlatformMessage (instead of returning null).

virtual ~Plugin() {}

// A function for hooking into keyboard input.
virtual void KeyboardHook(GLFWwindow *window, int key, int scancode,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Making things specific to the text plugin part of Plugin would be unfortunate, since other plugins shouldn't need this. Since at some point text input will be a specific API rather than part of the plugin system I'd rather we treat this specially up front. We can have this be part of the subclass, and the main embedding code can know about the text input plugin as a specific object rather than just a generic plugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to an abstract class that the text input plugin now uses.

@@ -72,6 +76,22 @@ static void GLFWwindowSizeCallback(GLFWwindow *window, int width, int height) {
FlutterEngineSendWindowMetricsEvent(state->engine, &event);
}

// Callback invoked when a plugin wants to send a message to the Flutter Engine.
static void GLFWOnPlatformCallback(GLFWwindow *window,
Copy link
Collaborator

Choose a reason for hiding this comment

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

It seems confusing to have this named using the same style as the engine callbacks. Why not just SendFlutterEngineMessage or something like that?

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 was a bit on the fence about the naming of this function myself. I think that works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

.message_size = output.size(),
};
FlutterEngineSendPlatformMessage(state->engine, &platform_message_response);
// TODO(awdavies): This needs updating for Issue #45.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need this code as part of this patch? The macOS text input plugin always sends back null for now just like the other ones, so I would expect we could leave this out and revisit later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.


#include <flutter_desktop_embedding/common/platform_protocol.h>

static constexpr char kComposingBaseKey[] = "composingBase";
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's put a comment referencing #47 on these constants (which I'll do on the macOS side as well sometime soon). I want to make sure anyone looking at the code is aware that this isn't actually supported.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

private:
void DeleteSelected();

std::string rep_;
Copy link
Collaborator

Choose a reason for hiding this comment

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

No abbreviations in variable names, per style guide.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


void TextInputModel::DeleteSelected() {
selection_extent_ = rep_.erase(selection_base_, selection_extent_);
selection_base_ = selection_extent_;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Per offline discussion let's change to:
selection_base_ = ...
selection_extent_ = selection_base_
since it seemed more confusing to me as written.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Same with some other spots below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed! 😀

@awdavies
Copy link
Contributor Author

@stuartmorgan PTAL

Addressed comments.

Copy link
Collaborator

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

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

Just a couple notes left; looking really good!


void KeyboardHook(GLFWwindow *window, int key, int scancode, int action,
int mods) override;

Copy link
Collaborator

Choose a reason for hiding this comment

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

Given that there are multiple parents, I'd use labelled clusters for the overrides to make it clearer what's what:

// Plugin
Json::Value HandlePlatformMessage(const Json::Value &message) override;

// KeyboardHookHandler:
void KeyboardHook(GLFWwindow *window, int key, int scancode, int action,
int mods) override;
void CharHook(GLFWwindow *window, unsigned int code_point) override;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good! Of all the C++ I've done this is the first time I've actually used multiple inheritance

.message = reinterpret_cast<const uint8_t *>(output.c_str()),
.message_size = output.size(),
};
FlutterEngineSendPlatformMessage(state->engine, &platform_message_response);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Since this is all duplicate code with line 89, extract to a static helper? (Eventually I think this will move into Plugin, but I can revisit that when reworking the plugin API across 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.

I think just calling the static function would be sufficient, no? The code is the same, for sure.

@awdavies
Copy link
Contributor Author

awdavies commented May 2, 2018

@stuartmorgan Last pass! (maybe)

// Contains a channel string for the plugin and a JSON value to pass to the
// engine.
typedef std::function<void(const std::string &, const Json::Value &)>
PlatformCallback;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This can be removed now, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Woops! Forgot that was there. Good catch

virtual void set_flutter_engine(FlutterEngine engine) { engine_ = engine; }

protected:
// Runs the platform callback. Noop if the callback is not set.
Copy link
Collaborator

Choose a reason for hiding this comment

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

This comment is out of date. Also, maybe this function should be called SendMessageToEngine or something similar now, since it's not really clear what CallPlatformCallback means in this structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!


protected:
// Runs the platform callback. Noop if the callback is not set.
void CallPlatformCallback(const Json::Value &json) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's move this to a .cc, since it doesn't need to be in the header and we don't want it inlined.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

CC file made!

@awdavies awdavies merged commit 688213e into google:master May 3, 2018
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants