Skip to content
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

Resize egl window when software keyboard is shown #7

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 102 additions & 34 deletions shell/platform/tizen/channels/text_input_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

#include "text_input_channel.h"

#include <Ecore.h>
#include <Ecore_IMF_Evas.h>

#include "flutter/shell/platform/tizen/logger.h"
#include "flutter/shell/platform/tizen/tizen_embedder_engine.h"
#include "flutter/shell/platform/tizen/tizen_surface.h"
#include "stdlib.h"
#include "string.h"

static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState";
static constexpr char kClearClientMethod[] = "TextInput.clearClient";
static constexpr char kSetClientMethod[] = "TextInput.setClient";
Expand Down Expand Up @@ -54,15 +56,15 @@ static bool IsASCIIPrintableKey(char c) {
return false;
}

static void CommitCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info) {
void TextInputChannel::CommitCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info) {
TextInputChannel* self = (TextInputChannel*)data;
char* str = (char*)event_info;
self->OnCommit(str);
}

static void PreeditCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info) {
void TextInputChannel::PreeditCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info) {
TextInputChannel* self = (TextInputChannel*)data;
char* preedit_string = nullptr;
int cursor_pos;
Expand All @@ -73,34 +75,54 @@ static void PreeditCallback(void* data, Ecore_IMF_Context* ctx,
}
}

static void PrivateCommandCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info) {
void TextInputChannel::PrivateCommandCallback(void* data,
Ecore_IMF_Context* ctx,
void* event_info) {
// TODO
LoggerD("Unimplemented");
}

static void DeleteSurroundingCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info) {
void TextInputChannel::DeleteSurroundingCallback(void* data,
Ecore_IMF_Context* ctx,
void* event_info) {
// TODO
LoggerD("Unimplemented");
}

static void InputPanelStatChangedCallback(void* data,
Ecore_IMF_Context* context,
int value) {
void TextInputChannel::InputPanelStateChangedCallback(
void* data, Ecore_IMF_Context* context, int value) {
if (!data) {
LoggerD("[No Data]\n");
return;
}
TextInputChannel* self = (TextInputChannel*)data;
switch (value) {
case ECORE_IMF_INPUT_PANEL_STATE_SHOW:
case ECORE_IMF_INPUT_PANEL_STATE_SHOW: {
LoggerD("[PANEL_STATE_SHOW]\n");
self->ShowSoftwareKeyboard();
break;
if (self->engine_->device_profile ==
"mobile") { // FIXME : Needs improvement on other devices.
ecore_timer_add(
0.25,
[](void* data) -> Eina_Bool {
TextInputChannel* self = (TextInputChannel*)data;
int32_t surface_w = self->engine_->tizen_surface->GetWidth();
int32_t surface_h = self->engine_->tizen_surface->GetHeight() -
self->current_keyboard_geometry_.h;
self->engine_->tizen_surface->SetSize(surface_w, surface_h);
if (self->rotation == 90 || self->rotation == 270) {

Choose a reason for hiding this comment

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

How to initialize 'rotation' value and handle it changed?

Copy link
Author

Choose a reason for hiding this comment

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

This is changed in TizenEmbedderEngine::SetWindowOrientation like the rotation of the touch event handler.

self->engine_->SendWindowMetrics(surface_h, surface_w, 0);
} else {
self->engine_->SendWindowMetrics(surface_w, surface_h, 0);
}

return ECORE_CALLBACK_CANCEL;
},
self);
}
} break;
case ECORE_IMF_INPUT_PANEL_STATE_HIDE:
self->HideSoftwareKeyboard(); // FIXME: Fallback for HW back-key
LoggerD("[PANEL_STATE_HIDE]\n");
self->HideSoftwareKeyboard();
break;
case ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW:
LoggerD("[PANEL_STATE_WILL_SHOW]\n");
Expand All @@ -111,8 +133,28 @@ static void InputPanelStatChangedCallback(void* data,
}
}

static Eina_Bool RetrieveSurroundingCallback(void* data, Ecore_IMF_Context* ctx,
char** text, int* cursor_pos) {
void TextInputChannel::InputPanelGeometryChangedCallback(
void* data, Ecore_IMF_Context* context, int value) {
if (!data) {
LoggerD("[No Data]\n");
return;
}
TextInputChannel* self = (TextInputChannel*)data;
ecore_imf_context_input_panel_geometry_get(
self->imfContext_, &self->current_keyboard_geometry_.x,
&self->current_keyboard_geometry_.y, &self->current_keyboard_geometry_.w,
&self->current_keyboard_geometry_.h);

LoggerD(
"[Current keyboard geometry] x:%d y:%d w:%d h:%d\n",
self->current_keyboard_geometry_.x, self->current_keyboard_geometry_.y,
self->current_keyboard_geometry_.w, self->current_keyboard_geometry_.h);
}

Eina_Bool TextInputChannel::RetrieveSurroundingCallback(void* data,
Ecore_IMF_Context* ctx,
char** text,
int* cursor_pos) {
// TODO
if (text) {
*text = strdup("");
Expand Down Expand Up @@ -226,21 +268,15 @@ Ecore_IMF_Device_Subclass EoreDeviceSubClassToEcoreIMFDeviceSubClass(
}

TextInputChannel::TextInputChannel(flutter::BinaryMessenger* messenger,
Ecore_Wl2_Window* ecoreWindow)
TizenEmbedderEngine* engine)
: channel_(std::make_unique<flutter::MethodChannel<rapidjson::Document>>(
messenger, kChannelName, &flutter::JsonMethodCodec::GetInstance())),
active_model_(nullptr),
isSoftwareKeyboardShowing_(false),
lastPreeditStringLength_(0),
imfContext_(nullptr),
isWearable_(false),
inSelectMode_(false) {
const char* elmProfile = getenv("ELM_PROFILE");
if (!elmProfile || strcmp(elmProfile, "wearable") == 0) {
LoggerD("ELM_PROFILE is wearable");
isWearable_ = true;
}

inSelectMode_(false),
engine_(engine) {
channel_->SetMethodCallHandler(
[this](
const flutter::MethodCall<rapidjson::Document>& call,
Expand All @@ -256,6 +292,8 @@ TextInputChannel::TextInputChannel(flutter::BinaryMessenger* messenger,
imfContext_ = ecore_imf_context_add(getImfMethod());
}
if (imfContext_) {
Ecore_Wl2_Window* ecoreWindow =
((TizenSurfaceGL*)engine_->tizen_surface.get())->wl2_window();
ecore_imf_context_client_window_set(
imfContext_, (void*)ecore_wl2_window_id_get(ecoreWindow));
RegisterIMFCallback(ecoreWindow);
Expand Down Expand Up @@ -391,8 +429,8 @@ void TextInputChannel::SendStateUpdate(const flutter::TextInputModel& model) {
}

bool TextInputChannel::FilterEvent(Ecore_Event_Key* keyDownEvent) {
LoggerD("NonIMFFallback key name [%s]", keyDownEvent->keyname);
bool handled = false;

const char* device = ecore_device_name_get(keyDownEvent->dev);

Ecore_IMF_Event_Key_Down ecoreKeyDownEvent;
Expand All @@ -414,7 +452,7 @@ bool TextInputChannel::FilterEvent(Ecore_Event_Key* keyDownEvent) {

bool isIME = strcmp(device, "ime") == 0;
if (isIME && strcmp(keyDownEvent->key, "Select") == 0) {
if (isWearable_) {
if (engine_->device_profile == "wearable") {
inSelectMode_ = true;
} else {
SelectPressed(active_model_.get());
Expand Down Expand Up @@ -563,9 +601,33 @@ void TextInputChannel::HideSoftwareKeyboard() {
isSoftwareKeyboardShowing_);
if (imfContext_ && isSoftwareKeyboardShowing_) {
isSoftwareKeyboardShowing_ = false;
ecore_imf_context_reset(imfContext_);
ecore_imf_context_focus_out(imfContext_);
ecore_imf_context_input_panel_hide(imfContext_);

if (engine_->device_profile ==
"mobile") { // FIXME : Needs improvement on other devices.
auto w = engine_->tizen_surface->GetWidth();
auto h = engine_->tizen_surface->GetHeight();

if (rotation == 90 || rotation == 270) {
engine_->SendWindowMetrics(h, w, 0);
} else {
engine_->SendWindowMetrics(w, h, 0);
}
engine_->tizen_surface->SetSize(w, h);
ecore_timer_add(
0.05,
Copy link
Member

Choose a reason for hiding this comment

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

Is this a heuristic value? Is the delay needed?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, it is heuristic. I tested it on devices and emulators, it is better when I set some delay. it can be removed anytime. However, when using the timer, it looked better even with 0 value

[](void* data) -> Eina_Bool {
Ecore_IMF_Context* imfContext = (Ecore_IMF_Context*)data;
ecore_imf_context_reset(imfContext);
ecore_imf_context_focus_out(imfContext);
ecore_imf_context_input_panel_hide(imfContext);
return ECORE_CALLBACK_CANCEL;
},
imfContext_);
} else {
ecore_imf_context_reset(imfContext_);
ecore_imf_context_focus_out(imfContext_);
ecore_imf_context_input_panel_hide(imfContext_);
}
}
}

Expand All @@ -583,7 +645,10 @@ void TextInputChannel::RegisterIMFCallback(Ecore_Wl2_Window* ecoreWindow) {
PrivateCommandCallback, this);
ecore_imf_context_input_panel_event_callback_add(
imfContext_, ECORE_IMF_INPUT_PANEL_STATE_EVENT,
InputPanelStatChangedCallback, this);
InputPanelStateChangedCallback, this);
ecore_imf_context_input_panel_event_callback_add(
imfContext_, ECORE_IMF_INPUT_PANEL_GEOMETRY_EVENT,
InputPanelGeometryChangedCallback, this);
ecore_imf_context_retrieve_surrounding_callback_set(
imfContext_, RetrieveSurroundingCallback, this);

Expand Down Expand Up @@ -613,5 +678,8 @@ void TextInputChannel::UnregisterIMFCallback() {
PrivateCommandCallback);
ecore_imf_context_input_panel_event_callback_del(
imfContext_, ECORE_IMF_INPUT_PANEL_STATE_EVENT,
InputPanelStatChangedCallback);
InputPanelStateChangedCallback);
ecore_imf_context_input_panel_event_callback_del(
imfContext_, ECORE_IMF_INPUT_PANEL_GEOMETRY_EVENT,
InputPanelGeometryChangedCallback);
}
33 changes: 30 additions & 3 deletions shell/platform/tizen/channels/text_input_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,26 @@
#include "flutter/shell/platform/common/cpp/json_method_codec.h"
#include "flutter/shell/platform/common/cpp/text_input_model.h"

class TizenEmbedderEngine;
class TextInputChannel {
public:
struct SoftwareKeyboardGeometry {
int32_t x = 0, y = 0, w = 0, h = 0;
};
explicit TextInputChannel(flutter::BinaryMessenger* messenger,
Ecore_Wl2_Window* ecoreWindow);
TizenEmbedderEngine* engine);
virtual ~TextInputChannel();
void OnKeyDown(Ecore_Event_Key* key);
void OnCommit(const char* str);
void OnPredit(const char* str, int cursorPos);
void ShowSoftwareKeyboard();
void HideSoftwareKeyboard();
bool isSoftwareKeyboardShowing() { return isSoftwareKeyboardShowing_; }
SoftwareKeyboardGeometry GetCurrentKeyboardGeometry() {
return current_keyboard_geometry_;
}

int32_t rotation = 0;

private:
void HandleMethodCall(
Expand All @@ -44,14 +53,32 @@ class TextInputChannel {
std::unique_ptr<flutter::MethodChannel<rapidjson::Document>> channel_;
std::unique_ptr<flutter::TextInputModel> active_model_;

private:
static void CommitCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info);
static void PreeditCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info);
static void PrivateCommandCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info);
static void DeleteSurroundingCallback(void* data, Ecore_IMF_Context* ctx,
void* event_info);
static void InputPanelStateChangedCallback(void* data,
Ecore_IMF_Context* context,
int value);
static void InputPanelGeometryChangedCallback(void* data,
Ecore_IMF_Context* context,
int value);
static Eina_Bool RetrieveSurroundingCallback(void* data,
Ecore_IMF_Context* ctx,
char** text, int* cursor_pos);

int client_id_;
std::string input_type_;
std::string input_action_;
bool isSoftwareKeyboardShowing_;
int lastPreeditStringLength_;
Ecore_IMF_Context* imfContext_;
bool isWearable_;
bool inSelectMode_;
TizenEmbedderEngine* engine_;
SoftwareKeyboardGeometry current_keyboard_geometry_;
};
#endif
Loading