Standardize IME callbacks for off-screen rendering (issue #1675)
This commit is contained in:
parent
e69de63b15
commit
d6b17a8fb5
|
@ -30,3 +30,4 @@ YuTeh Shen <shenyute@gmail.com>
|
|||
Andrei Kurushin <ajax16384@gmail.com>
|
||||
Gonzo Berman <gberman@factset.com>
|
||||
Jakub Trzebiatowski <kuba.trzebiatowski@gmail.com>
|
||||
Nishant Kaushik <nishantk@adobe.com>
|
||||
|
|
3
BUILD.gn
3
BUILD.gn
|
@ -710,8 +710,6 @@ static_library("libcef_static") {
|
|||
"libcef/browser/osr/browser_platform_delegate_osr_mac.h",
|
||||
"libcef/browser/osr/browser_platform_delegate_osr_mac.mm",
|
||||
"libcef/browser/osr/render_widget_host_view_osr_mac.mm",
|
||||
"libcef/browser/osr/text_input_client_osr_mac.mm",
|
||||
"libcef/browser/osr/text_input_client_osr_mac.h",
|
||||
"libcef/common/util_mac.h",
|
||||
"libcef/common/util_mac.mm",
|
||||
]
|
||||
|
@ -1794,6 +1792,7 @@ if (is_mac) {
|
|||
"rpcrt4.lib",
|
||||
"opengl32.lib",
|
||||
"glu32.lib",
|
||||
"imm32.lib",
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
@ -248,6 +248,8 @@
|
|||
'tests/cefclient/browser/main_message_loop_multithreaded_win.h',
|
||||
'tests/cefclient/browser/osr_dragdrop_win.cc',
|
||||
'tests/cefclient/browser/osr_dragdrop_win.h',
|
||||
'tests/cefclient/browser/osr_ime_handler_win.cc',
|
||||
'tests/cefclient/browser/osr_ime_handler_win.h',
|
||||
'tests/cefclient/browser/osr_window_win.cc',
|
||||
'tests/cefclient/browser/osr_window_win.h',
|
||||
'tests/cefclient/browser/resource_util_win.cc',
|
||||
|
@ -284,6 +286,8 @@
|
|||
'tests/cefclient/browser/root_window_mac.mm',
|
||||
'tests/cefclient/browser/temp_window_mac.h',
|
||||
'tests/cefclient/browser/temp_window_mac.mm',
|
||||
'tests/cefclient/browser/text_input_client_osr_mac.h',
|
||||
'tests/cefclient/browser/text_input_client_osr_mac.mm',
|
||||
'tests/cefclient/browser/window_test_runner_mac.h',
|
||||
'tests/cefclient/browser/window_test_runner_mac.mm',
|
||||
'tests/cefclient/cefclient_mac.mm',
|
||||
|
|
|
@ -618,24 +618,62 @@ typedef struct _cef_browser_host_t {
|
|||
struct _cef_browser_host_t* self, int frame_rate);
|
||||
|
||||
///
|
||||
// Get the NSTextInputContext implementation for enabling IME on Mac when
|
||||
// window rendering is disabled.
|
||||
// Begins a new composition or updates the existing composition. Blink has a
|
||||
// special node (a composition node) that allows the input function to change
|
||||
// text without affecting other DOM nodes. |text| is the optional text that
|
||||
// will be inserted into the composition node. |underlines| is an optional set
|
||||
// of ranges that will be underlined in the resulting text.
|
||||
// |replacement_range| is an optional range of the existing text that will be
|
||||
// replaced. |selection_range| is an optional range of the resulting text that
|
||||
// will be selected after insertion or replacement. The |replacement_range|
|
||||
// value is only used on OS X.
|
||||
//
|
||||
// This function may be called multiple times as the composition changes. When
|
||||
// the client is done making changes the composition should either be canceled
|
||||
// or completed. To cancel the composition call ImeCancelComposition. To
|
||||
// complete the composition call either ImeCommitText or
|
||||
// ImeFinishComposingText. Completion is usually signaled when:
|
||||
// A. The client receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR
|
||||
// flag (on Windows), or;
|
||||
// B. The client receives a "commit" signal of GtkIMContext (on Linux), or;
|
||||
// C. insertText of NSTextInput is called (on Mac).
|
||||
//
|
||||
// This function is only used when window rendering is disabled.
|
||||
///
|
||||
cef_text_input_context_t (CEF_CALLBACK *get_nstext_input_context)(
|
||||
struct _cef_browser_host_t* self);
|
||||
void (CEF_CALLBACK *ime_set_composition)(struct _cef_browser_host_t* self,
|
||||
const cef_string_t* text, size_t underlinesCount,
|
||||
cef_composition_underline_t const* underlines,
|
||||
const cef_range_t* replacement_range,
|
||||
const cef_range_t* selection_range);
|
||||
|
||||
///
|
||||
// Handles a keyDown event prior to passing it through the NSTextInputClient
|
||||
// machinery.
|
||||
// Completes the existing composition by optionally inserting the specified
|
||||
// |text| into the composition node. |replacement_range| is an optional range
|
||||
// of the existing text that will be replaced. |relative_cursor_pos| is where
|
||||
// the cursor will be positioned relative to the current cursor position. See
|
||||
// comments on ImeSetComposition for usage. The |replacement_range| and
|
||||
// |relative_cursor_pos| values are only used on OS X. This function is only
|
||||
// used when window rendering is disabled.
|
||||
///
|
||||
void (CEF_CALLBACK *handle_key_event_before_text_input_client)(
|
||||
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent);
|
||||
void (CEF_CALLBACK *ime_commit_text)(struct _cef_browser_host_t* self,
|
||||
const cef_string_t* text, const cef_range_t* replacement_range,
|
||||
int relative_cursor_pos);
|
||||
|
||||
///
|
||||
// Performs any additional actions after NSTextInputClient handles the event.
|
||||
// Completes the existing composition by applying the current composition node
|
||||
// contents. If |keep_selection| is false (0) the current selection, if any,
|
||||
// will be discarded. See comments on ImeSetComposition for usage. This
|
||||
// function is only used when window rendering is disabled.
|
||||
///
|
||||
void (CEF_CALLBACK *handle_key_event_after_text_input_client)(
|
||||
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent);
|
||||
void (CEF_CALLBACK *ime_finish_composing_text)(
|
||||
struct _cef_browser_host_t* self, int keep_selection);
|
||||
|
||||
///
|
||||
// Cancels the existing composition and discards the composition node contents
|
||||
// without applying them. See comments on ImeSetComposition for usage. This
|
||||
// function is only used when window rendering is disabled.
|
||||
///
|
||||
void (CEF_CALLBACK *ime_cancel_composition)(struct _cef_browser_host_t* self);
|
||||
|
||||
///
|
||||
// Call this function when the user drags the mouse into the web view (before
|
||||
|
|
|
@ -161,6 +161,16 @@ typedef struct _cef_render_handler_t {
|
|||
void (CEF_CALLBACK *on_scroll_offset_changed)(
|
||||
struct _cef_render_handler_t* self, struct _cef_browser_t* browser,
|
||||
double x, double y);
|
||||
|
||||
///
|
||||
// Called when the IME composition range has changed. |selected_range| is the
|
||||
// range of characters that have been selected. |character_bounds| is the
|
||||
// bounds of each character in view coordinates.
|
||||
///
|
||||
void (CEF_CALLBACK *on_ime_composition_range_changed)(
|
||||
struct _cef_render_handler_t* self, struct _cef_browser_t* browser,
|
||||
const cef_range_t* selected_range, size_t character_boundsCount,
|
||||
cef_rect_t const* character_bounds);
|
||||
} cef_render_handler_t;
|
||||
|
||||
|
||||
|
|
|
@ -669,24 +669,66 @@ class CefBrowserHost : public virtual CefBase {
|
|||
virtual void SetWindowlessFrameRate(int frame_rate) =0;
|
||||
|
||||
///
|
||||
// Get the NSTextInputContext implementation for enabling IME on Mac when
|
||||
// window rendering is disabled.
|
||||
// Begins a new composition or updates the existing composition. Blink has a
|
||||
// special node (a composition node) that allows the input method to change
|
||||
// text without affecting other DOM nodes. |text| is the optional text that
|
||||
// will be inserted into the composition node. |underlines| is an optional set
|
||||
// of ranges that will be underlined in the resulting text.
|
||||
// |replacement_range| is an optional range of the existing text that will be
|
||||
// replaced. |selection_range| is an optional range of the resulting text that
|
||||
// will be selected after insertion or replacement. The |replacement_range|
|
||||
// value is only used on OS X.
|
||||
//
|
||||
// This method may be called multiple times as the composition changes. When
|
||||
// the client is done making changes the composition should either be canceled
|
||||
// or completed. To cancel the composition call ImeCancelComposition. To
|
||||
// complete the composition call either ImeCommitText or
|
||||
// ImeFinishComposingText. Completion is usually signaled when:
|
||||
// A. The client receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR
|
||||
// flag (on Windows), or;
|
||||
// B. The client receives a "commit" signal of GtkIMContext (on Linux), or;
|
||||
// C. insertText of NSTextInput is called (on Mac).
|
||||
//
|
||||
// This method is only used when window rendering is disabled.
|
||||
///
|
||||
/*--cef(default_retval=NULL)--*/
|
||||
virtual CefTextInputContext GetNSTextInputContext() =0;
|
||||
/*--cef(optional_param=text, optional_param=underlines)--*/
|
||||
virtual void ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) =0;
|
||||
|
||||
///
|
||||
// Handles a keyDown event prior to passing it through the NSTextInputClient
|
||||
// machinery.
|
||||
// Completes the existing composition by optionally inserting the specified
|
||||
// |text| into the composition node. |replacement_range| is an optional range
|
||||
// of the existing text that will be replaced. |relative_cursor_pos| is where
|
||||
// the cursor will be positioned relative to the current cursor position. See
|
||||
// comments on ImeSetComposition for usage. The |replacement_range| and
|
||||
// |relative_cursor_pos| values are only used on OS X.
|
||||
// This method is only used when window rendering is disabled.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) =0;
|
||||
/*--cef(optional_param=text)--*/
|
||||
virtual void ImeCommitText(const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos) =0;
|
||||
|
||||
///
|
||||
// Performs any additional actions after NSTextInputClient handles the event.
|
||||
// Completes the existing composition by applying the current composition node
|
||||
// contents. If |keep_selection| is false the current selection, if any, will
|
||||
// be discarded. See comments on ImeSetComposition for usage.
|
||||
// This method is only used when window rendering is disabled.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) =0;
|
||||
virtual void ImeFinishComposingText(bool keep_selection) =0;
|
||||
|
||||
///
|
||||
// Cancels the existing composition and discards the composition node
|
||||
// contents without applying them. See comments on ImeSetComposition for
|
||||
// usage.
|
||||
// This method is only used when window rendering is disabled.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void ImeCancelComposition() =0;
|
||||
|
||||
///
|
||||
// Call this method when the user drags the mouse into the web view (before
|
||||
|
|
|
@ -176,6 +176,16 @@ class CefRenderHandler : public virtual CefBase {
|
|||
virtual void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser,
|
||||
double x,
|
||||
double y) {}
|
||||
|
||||
///
|
||||
// Called when the IME composition range has changed. |selected_range| is the
|
||||
// range of characters that have been selected. |character_bounds| is the
|
||||
// bounds of each character in view coordinates.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selected_range,
|
||||
const RectList& character_bounds) {}
|
||||
};
|
||||
|
||||
#endif // CEF_INCLUDE_CEF_RENDER_HANDLER_H_
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#define CefCursorHandle cef_cursor_handle_t
|
||||
#define CefEventHandle cef_event_handle_t
|
||||
#define CefWindowHandle cef_window_handle_t
|
||||
#define CefTextInputContext cef_text_input_context_t
|
||||
|
||||
struct CefMainArgsTraits {
|
||||
typedef cef_main_args_t struct_type;
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#define CefCursorHandle cef_cursor_handle_t
|
||||
#define CefEventHandle cef_event_handle_t
|
||||
#define CefWindowHandle cef_window_handle_t
|
||||
#define CefTextInputContext cef_text_input_context_t
|
||||
|
||||
struct CefMainArgsTraits {
|
||||
typedef cef_main_args_t struct_type;
|
||||
|
|
|
@ -2682,6 +2682,33 @@ typedef enum {
|
|||
CEF_CDM_REGISTRATION_ERROR_NOT_SUPPORTED,
|
||||
} cef_cdm_registration_error_t;
|
||||
|
||||
///
|
||||
// Structure representing IME composition underline information. This is a thin
|
||||
// wrapper around Blink's WebCompositionUnderline class and should be kept in
|
||||
// sync with that.
|
||||
///
|
||||
typedef struct _cef_composition_underline_t {
|
||||
///
|
||||
// Underline character range.
|
||||
///
|
||||
cef_range_t range;
|
||||
|
||||
///
|
||||
// Text color.
|
||||
///
|
||||
cef_color_t color;
|
||||
|
||||
///
|
||||
// Background color.
|
||||
///
|
||||
cef_color_t background_color;
|
||||
|
||||
///
|
||||
// Set to true (1) for thick underline.
|
||||
///
|
||||
int thick;
|
||||
} cef_composition_underline_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -60,7 +60,6 @@ extern "C" {
|
|||
// thread-safe and must only be accessed on the browser process UI thread.
|
||||
///
|
||||
CEF_EXPORT XDisplay* cef_get_xdisplay();
|
||||
#define cef_text_input_context_t void*
|
||||
|
||||
///
|
||||
// Structure representing CefExecuteProcess arguments.
|
||||
|
|
|
@ -43,22 +43,18 @@
|
|||
@class NSCursor;
|
||||
@class NSEvent;
|
||||
@class NSView;
|
||||
@class NSTextInputContext;
|
||||
#else
|
||||
class NSCursor;
|
||||
class NSEvent;
|
||||
struct NSView;
|
||||
class NSTextInputContext;
|
||||
#endif
|
||||
#define cef_cursor_handle_t NSCursor*
|
||||
#define cef_event_handle_t NSEvent*
|
||||
#define cef_window_handle_t NSView*
|
||||
#define cef_text_input_context_t NSTextInputContext*
|
||||
#else
|
||||
#define cef_cursor_handle_t void*
|
||||
#define cef_event_handle_t void*
|
||||
#define cef_window_handle_t void*
|
||||
#define cef_text_input_context_t void*
|
||||
#endif
|
||||
|
||||
#define kNullCursorHandle NULL
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
#define cef_cursor_handle_t HCURSOR
|
||||
#define cef_event_handle_t MSG*
|
||||
#define cef_window_handle_t HWND
|
||||
#define cef_text_input_context_t void*
|
||||
|
||||
#define kNullCursorHandle NULL
|
||||
#define kNullEventHandle NULL
|
||||
|
|
|
@ -975,4 +975,31 @@ struct CefBoxLayoutSettingsTraits {
|
|||
///
|
||||
typedef CefStructBase<CefBoxLayoutSettingsTraits> CefBoxLayoutSettings;
|
||||
|
||||
struct CefCompositionUnderlineTraits {
|
||||
typedef cef_composition_underline_t struct_type;
|
||||
|
||||
static inline void init(struct_type* s) {
|
||||
s->range = {0, 0};
|
||||
s->color = 0;
|
||||
s->background_color = 0;
|
||||
s->thick = 0;
|
||||
}
|
||||
|
||||
static inline void clear(struct_type* s) {
|
||||
}
|
||||
|
||||
static inline void set(const struct_type* src, struct_type* target,
|
||||
bool copy) {
|
||||
target->range = src->range;
|
||||
target->color = src->color;
|
||||
target->background_color = src->background_color;
|
||||
target->thick = src->thick;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
// Class representing IME composition underline.
|
||||
///
|
||||
typedef CefStructBase<CefCompositionUnderlineTraits> CefCompositionUnderline;
|
||||
|
||||
#endif // CEF_INCLUDE_INTERNAL_CEF_TYPES_WRAPPERS_H_
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#define CefCursorHandle cef_cursor_handle_t
|
||||
#define CefEventHandle cef_event_handle_t
|
||||
#define CefWindowHandle cef_window_handle_t
|
||||
#define CefTextInputContext cef_text_input_context_t
|
||||
|
||||
struct CefMainArgsTraits {
|
||||
typedef cef_main_args_t struct_type;
|
||||
|
|
|
@ -1797,65 +1797,87 @@ void CefBrowserHostImpl::FindReply(
|
|||
}
|
||||
}
|
||||
|
||||
CefTextInputContext CefBrowserHostImpl::GetNSTextInputContext() {
|
||||
#if defined(OS_MACOSX)
|
||||
void CefBrowserHostImpl::ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) {
|
||||
if (!IsWindowless()) {
|
||||
NOTREACHED() << "Window rendering is not disabled";
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CEF_CURRENTLY_ON_UIT()) {
|
||||
NOTREACHED() << "Called on invalid thread";
|
||||
return nullptr;
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefBrowserHostImpl::ImeSetComposition, this, text,
|
||||
underlines, replacement_range, selection_range));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!web_contents() || !platform_delegate_)
|
||||
return nullptr;
|
||||
return;
|
||||
|
||||
return platform_delegate_->GetNSTextInputContext();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
platform_delegate_->ImeSetComposition(text, underlines, replacement_range,
|
||||
selection_range);
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::HandleKeyEventBeforeTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
#if defined(OS_MACOSX)
|
||||
void CefBrowserHostImpl::ImeCommitText(const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos) {
|
||||
if (!IsWindowless()) {
|
||||
NOTREACHED() << "Window rendering is not disabled";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CEF_CURRENTLY_ON_UIT()) {
|
||||
NOTREACHED() << "Called on invalid thread";
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefBrowserHostImpl::ImeCommitText, this, text,
|
||||
replacement_range, relative_cursor_pos));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!web_contents() || !platform_delegate_)
|
||||
return;
|
||||
|
||||
platform_delegate_->HandleKeyEventBeforeTextInputClient(keyEvent);
|
||||
#endif
|
||||
platform_delegate_->ImeCommitText(text, replacement_range,
|
||||
relative_cursor_pos);
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::HandleKeyEventAfterTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
#if defined(OS_MACOSX)
|
||||
void CefBrowserHostImpl::ImeFinishComposingText(bool keep_selection) {
|
||||
if (!IsWindowless()) {
|
||||
NOTREACHED() << "Window rendering is not disabled";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CEF_CURRENTLY_ON_UIT()) {
|
||||
NOTREACHED() << "Called on invalid thread";
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefBrowserHostImpl::ImeFinishComposingText, this,
|
||||
keep_selection));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!web_contents() || !platform_delegate_)
|
||||
return;
|
||||
|
||||
return platform_delegate_->HandleKeyEventAfterTextInputClient(keyEvent);
|
||||
#endif
|
||||
platform_delegate_->ImeFinishComposingText(keep_selection);
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::ImeCancelComposition() {
|
||||
if (!IsWindowless()) {
|
||||
NOTREACHED() << "Window rendering is not disabled";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CEF_CURRENTLY_ON_UIT()) {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefBrowserHostImpl::ImeCancelComposition, this));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!web_contents() || !platform_delegate_)
|
||||
return;
|
||||
|
||||
platform_delegate_->ImeCancelComposition();
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
|
||||
|
|
|
@ -206,9 +206,14 @@ class CefBrowserHostImpl : public CefBrowserHost,
|
|||
void NotifyMoveOrResizeStarted() override;
|
||||
int GetWindowlessFrameRate() override;
|
||||
void SetWindowlessFrameRate(int frame_rate) override;
|
||||
CefTextInputContext GetNSTextInputContext() override;
|
||||
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) override;
|
||||
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) override;
|
||||
void ImeSetComposition(const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) override;
|
||||
void ImeCommitText(const CefString& text, const CefRange& replacement_range,
|
||||
int relative_cursor_pos) override;
|
||||
void ImeFinishComposingText(bool keep_selection) override;
|
||||
void ImeCancelComposition() override;
|
||||
void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
|
||||
const CefMouseEvent& event,
|
||||
DragOperationsMask allowed_ops) override;
|
||||
|
|
|
@ -134,22 +134,27 @@ void CefBrowserPlatformDelegate::SetWindowlessFrameRate(int frame_rate) {
|
|||
NOTREACHED();
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
CefTextInputContext CefBrowserPlatformDelegate::GetNSTextInputContext() {
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegate::HandleKeyEventBeforeTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
void CefBrowserPlatformDelegate::ImeSetComposition(const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegate::HandleKeyEventAfterTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
void CefBrowserPlatformDelegate::ImeCommitText(
|
||||
const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegate::ImeFinishComposingText(bool keep_selection) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegate::ImeCancelComposition() {
|
||||
NOTREACHED();
|
||||
}
|
||||
#endif
|
||||
|
||||
void CefBrowserPlatformDelegate::DragTargetDragEnter(
|
||||
CefRefPtr<CefDragData> drag_data,
|
||||
|
|
|
@ -222,13 +222,18 @@ class CefBrowserPlatformDelegate {
|
|||
// Set the windowless frame rate. Only used with windowless rendering.
|
||||
virtual void SetWindowlessFrameRate(int frame_rate);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// IME-related callbacks. See documentation in CefRenderHandler. Only used
|
||||
// with windowless rendering on OS X.
|
||||
virtual CefTextInputContext GetNSTextInputContext();
|
||||
virtual void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent);
|
||||
virtual void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent);
|
||||
#endif
|
||||
// IME-related callbacks. See documentation in CefBrowser and
|
||||
// CefRenderHandler. Only used with windowless rendering.
|
||||
virtual void ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range);
|
||||
virtual void ImeCommitText(const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos);
|
||||
virtual void ImeFinishComposingText(bool keep_selection);
|
||||
virtual void ImeCancelComposition();
|
||||
|
||||
// Drag/drop-related callbacks. See documentation in CefRenderHandler. Only
|
||||
// used with windowless rendering.
|
||||
|
|
|
@ -209,6 +209,40 @@ void CefBrowserPlatformDelegateOsr::SetWindowlessFrameRate(int frame_rate) {
|
|||
view->UpdateFrameRate();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view) {
|
||||
view->ImeSetComposition(text, underlines,
|
||||
replacement_range, selection_range);
|
||||
}
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::ImeCommitText(
|
||||
const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos) {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view)
|
||||
view->ImeCommitText(text, replacement_range, relative_cursor_pos);
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::ImeFinishComposingText(
|
||||
bool keep_selection) {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view)
|
||||
view->ImeFinishComposingText(keep_selection);
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::ImeCancelComposition() {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view)
|
||||
view->ImeCancelComposition();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::DragTargetDragEnter(
|
||||
CefRefPtr<CefDragData> drag_data,
|
||||
const CefMouseEvent& event,
|
||||
|
|
|
@ -56,6 +56,15 @@ class CefBrowserPlatformDelegateOsr :
|
|||
void NotifyScreenInfoChanged() override;
|
||||
void Invalidate(cef_paint_element_type_t type) override;
|
||||
void SetWindowlessFrameRate(int frame_rate) override;
|
||||
void ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) override;
|
||||
void ImeCommitText(const CefString& text, const CefRange& replacement_range,
|
||||
int relative_cursor_pos) override;
|
||||
void ImeFinishComposingText(bool keep_selection) override;
|
||||
void ImeCancelComposition() override;
|
||||
void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
|
||||
const CefMouseEvent& event,
|
||||
cef_drag_operations_mask_t allowed_ops) override;
|
||||
|
|
|
@ -15,9 +15,6 @@ class CefBrowserPlatformDelegateOsrMac : public CefBrowserPlatformDelegateOsr {
|
|||
|
||||
// CefBrowserPlatformDelegate methods:
|
||||
CefWindowHandle GetHostWindowHandle() const override;
|
||||
CefTextInputContext GetNSTextInputContext() override;
|
||||
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) override;
|
||||
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) override;
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_BROWSER_NATIVE_BROWSER_PLATFORM_DELEGATE_OSR_MAC_H_
|
||||
|
|
|
@ -18,23 +18,3 @@ CefWindowHandle CefBrowserPlatformDelegateOsrMac::GetHostWindowHandle() const {
|
|||
return native_delegate_->window_info().parent_view;
|
||||
}
|
||||
|
||||
CefTextInputContext CefBrowserPlatformDelegateOsrMac::GetNSTextInputContext() {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view)
|
||||
return view->GetNSTextInputContext();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsrMac::HandleKeyEventBeforeTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view)
|
||||
view->HandleKeyEventBeforeTextInputClient(keyEvent);
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsrMac::HandleKeyEventAfterTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
CefRenderWidgetHostViewOSR* view = GetOSRHostView();
|
||||
if (view)
|
||||
view->HandleKeyEventAfterTextInputClient(keyEvent);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "content/browser/renderer_host/render_widget_host_delegate.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h"
|
||||
#include "content/browser/renderer_host/resize_lock.h"
|
||||
#include "content/common/input_messages.h"
|
||||
#include "content/common/view_messages.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
|
@ -468,9 +469,6 @@ CefRenderWidgetHostViewOSR::CefRenderWidgetHostViewOSR(
|
|||
is_showing_(!render_widget_host_->is_hidden()),
|
||||
is_destroyed_(false),
|
||||
is_scroll_offset_changed_pending_(false),
|
||||
#if defined(OS_MACOSX)
|
||||
text_input_context_osr_mac_(NULL),
|
||||
#endif
|
||||
weak_ptr_factory_(this) {
|
||||
DCHECK(render_widget_host_);
|
||||
DCHECK(!render_widget_host_->GetView());
|
||||
|
@ -580,10 +578,6 @@ gfx::NativeViewAccessible
|
|||
return gfx::NativeViewAccessible();
|
||||
}
|
||||
|
||||
ui::TextInputClient* CefRenderWidgetHostViewOSR::GetTextInputClient() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::Focus() {
|
||||
}
|
||||
|
||||
|
@ -828,15 +822,6 @@ void CefRenderWidgetHostViewOSR::UpdateCursor(
|
|||
void CefRenderWidgetHostViewOSR::SetIsLoading(bool is_loading) {
|
||||
}
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
void CefRenderWidgetHostViewOSR::TextInputStateChanged(
|
||||
const content::TextInputState& params) {
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
|
||||
}
|
||||
#endif // !defined(OS_MACOSX)
|
||||
|
||||
void CefRenderWidgetHostViewOSR::RenderProcessGone(
|
||||
base::TerminationStatus status,
|
||||
int error_code) {
|
||||
|
@ -884,12 +869,6 @@ gfx::Size CefRenderWidgetHostViewOSR::GetPhysicalBackingSize() const {
|
|||
return gfx::ConvertSizeToPixel(scale_factor_, GetRequestedRendererSize());
|
||||
}
|
||||
|
||||
#if !defined(OS_MACOSX)
|
||||
void CefRenderWidgetHostViewOSR::SelectionBoundsChanged(
|
||||
const ViewHostMsg_SelectionBounds_Params& params) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void CefRenderWidgetHostViewOSR::CopyFromCompositingSurface(
|
||||
const gfx::Rect& src_subrect,
|
||||
const gfx::Size& dst_size,
|
||||
|
@ -963,12 +942,71 @@ void CefRenderWidgetHostViewOSR::ShowDisambiguationPopup(
|
|||
}
|
||||
#endif
|
||||
|
||||
#if !defined(OS_MACOSX) && defined(USE_AURA)
|
||||
void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
|
||||
const gfx::Range& range,
|
||||
const std::vector<gfx::Rect>& character_bounds) {
|
||||
void CefRenderWidgetHostViewOSR::ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) {
|
||||
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeSetComposition");
|
||||
if (!render_widget_host_)
|
||||
return;
|
||||
|
||||
std::vector<blink::WebCompositionUnderline> web_underlines;
|
||||
web_underlines.reserve(underlines.size());
|
||||
for (const CefCompositionUnderline& line : underlines) {
|
||||
web_underlines.push_back(
|
||||
blink::WebCompositionUnderline(line.range.from,
|
||||
line.range.to,
|
||||
line.color,
|
||||
line.thick ? true : false,
|
||||
line.background_color));
|
||||
}
|
||||
gfx::Range range(replacement_range.from, replacement_range.to);
|
||||
|
||||
// Start Monitoring for composition updates before we set.
|
||||
RequestImeCompositionUpdate(true);
|
||||
|
||||
render_widget_host_->ImeSetComposition(text, web_underlines, range,
|
||||
selection_range.from,
|
||||
selection_range.to);
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeCommitText(
|
||||
const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos) {
|
||||
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeCommitText");
|
||||
if (!render_widget_host_)
|
||||
return;
|
||||
|
||||
gfx::Range range(replacement_range.from, replacement_range.to);
|
||||
render_widget_host_->ImeCommitText(text, range, relative_cursor_pos);
|
||||
|
||||
// Stop Monitoring for composition updates after we are done.
|
||||
RequestImeCompositionUpdate(false);
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeFinishComposingText(bool keep_selection) {
|
||||
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeFinishComposingText");
|
||||
if (!render_widget_host_)
|
||||
return;
|
||||
|
||||
render_widget_host_->ImeFinishComposingText(keep_selection);
|
||||
|
||||
// Stop Monitoring for composition updates after we are done.
|
||||
RequestImeCompositionUpdate(false);
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
|
||||
TRACE_EVENT0("libcef", "CefRenderWidgetHostViewOSR::ImeCancelComposition");
|
||||
if (!render_widget_host_)
|
||||
return;
|
||||
|
||||
render_widget_host_->ImeCancelComposition();
|
||||
|
||||
// Stop Monitoring for composition updates after we are done.
|
||||
RequestImeCompositionUpdate(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
void CefRenderWidgetHostViewOSR::SetNeedsBeginFrames(bool enabled) {
|
||||
SetFrameRate();
|
||||
|
@ -1516,3 +1554,34 @@ void CefRenderWidgetHostViewOSR::InvalidateInternal(
|
|||
copy_frame_generator_->GenerateCopyFrame(true, bounds_in_pixels);
|
||||
}
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::RequestImeCompositionUpdate(
|
||||
bool start_monitoring) {
|
||||
if (!render_widget_host_)
|
||||
return;
|
||||
render_widget_host_->Send(
|
||||
new InputMsg_RequestCompositionUpdate(render_widget_host_->GetRoutingID(),
|
||||
false, start_monitoring));
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
|
||||
const gfx::Range& range,
|
||||
const std::vector<gfx::Rect>& character_bounds) {
|
||||
if (browser_impl_.get()) {
|
||||
CefRange cef_range(range.start(), range.end());
|
||||
CefRenderHandler::RectList rcList;
|
||||
|
||||
for (size_t i = 0; i < character_bounds.size(); ++i) {
|
||||
rcList.push_back(CefRect(character_bounds[i].x(), character_bounds[i].y(),
|
||||
character_bounds[i].width(),
|
||||
character_bounds[i].height()));
|
||||
}
|
||||
|
||||
CefRefPtr<CefRenderHandler> handler =
|
||||
browser_impl_->GetClient()->GetRenderHandler();
|
||||
if (handler.get()) {
|
||||
handler->OnImeCompositionRangeChanged(browser_impl_->GetBrowser(),
|
||||
cef_range, rcList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,6 @@ class CefRenderWidgetHostViewOSR
|
|||
gfx::Vector2dF GetLastScrollOffset() const override;
|
||||
gfx::NativeView GetNativeView() const override;
|
||||
gfx::NativeViewAccessible GetNativeViewAccessible() override;
|
||||
ui::TextInputClient* GetTextInputClient() override;
|
||||
void Focus() override;
|
||||
bool HasFocus() const override;
|
||||
bool IsSurfaceAvailableForCopy() const override;
|
||||
|
@ -133,23 +132,13 @@ class CefRenderWidgetHostViewOSR
|
|||
content::RenderWidgetHostView* reference_host_view) override;
|
||||
void UpdateCursor(const content::WebCursor& cursor) override;
|
||||
void SetIsLoading(bool is_loading) override;
|
||||
void TextInputStateChanged(const content::TextInputState& params) override;
|
||||
void ImeCancelComposition() override;
|
||||
void RenderProcessGone(base::TerminationStatus status,
|
||||
int error_code) override;
|
||||
void Destroy() override;
|
||||
void SetTooltipText(const base::string16& tooltip_text) override;
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void SelectionChanged(const base::string16& text,
|
||||
size_t offset,
|
||||
const gfx::Range& range) override;
|
||||
#endif
|
||||
|
||||
gfx::Size GetRequestedRendererSize() const override;
|
||||
gfx::Size GetPhysicalBackingSize() const override;
|
||||
void SelectionBoundsChanged(
|
||||
const ViewHostMsg_SelectionBounds_Params& params) override;
|
||||
void CopyFromCompositingSurface(
|
||||
const gfx::Rect& src_subrect,
|
||||
const gfx::Size& dst_size,
|
||||
|
@ -177,12 +166,9 @@ class CefRenderWidgetHostViewOSR
|
|||
void ShowDisambiguationPopup(const gfx::Rect& rect_pixels,
|
||||
const SkBitmap& zoomed_bitmap) override;
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX) || defined(USE_AURA)
|
||||
void ImeCompositionRangeChanged(
|
||||
const gfx::Range& range,
|
||||
const std::vector<gfx::Rect>& character_bounds) override;
|
||||
#endif
|
||||
|
||||
void SetNeedsBeginFrames(bool enabled) override;
|
||||
|
||||
|
@ -233,19 +219,16 @@ class CefRenderWidgetHostViewOSR
|
|||
return popup_type_ != blink::WebPopupTypeNone;
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
NSTextInputContext* GetNSTextInputContext();
|
||||
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent);
|
||||
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent);
|
||||
|
||||
bool GetCachedFirstRectForCharacterRange(gfx::Range range, gfx::Rect* rect,
|
||||
gfx::Range* actual_range) const;
|
||||
|
||||
const std::string& selected_text() const { return selected_text_; }
|
||||
const gfx::Range& composition_range() const { return composition_range_; }
|
||||
const base::string16& selection_text() const { return selection_text_; }
|
||||
size_t selection_text_offset() const { return selection_text_offset_; }
|
||||
#endif // defined(OS_MACOSX)
|
||||
void ImeSetComposition(
|
||||
const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range);
|
||||
void ImeCommitText(const CefString& text,
|
||||
const CefRange& replacement_range,
|
||||
int relative_cursor_pos);
|
||||
void ImeFinishComposingText(bool keep_selection);
|
||||
void ImeCancelComposition();
|
||||
|
||||
void AddGuestHostView(CefRenderWidgetHostViewOSR* guest_host);
|
||||
void RemoveGuestHostView(CefRenderWidgetHostViewOSR* guest_host);
|
||||
|
@ -293,30 +276,11 @@ class CefRenderWidgetHostViewOSR
|
|||
|
||||
void InvalidateInternal(const gfx::Rect& bounds_in_pixels);
|
||||
|
||||
void RequestImeCompositionUpdate(bool start_monitoring);
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
friend class MacHelper;
|
||||
|
||||
// Returns composition character boundary rectangle. The |range| is
|
||||
// composition based range. Also stores |actual_range| which is corresponding
|
||||
// to actually used range for returned rectangle.
|
||||
gfx::Rect GetFirstRectForCompositionRange(const gfx::Range& range,
|
||||
gfx::Range* actual_range) const;
|
||||
|
||||
// Converts from given whole character range to composition oriented range. If
|
||||
// the conversion failed, return gfx::Range::InvalidRange.
|
||||
gfx::Range ConvertCharacterRangeToCompositionRange(
|
||||
const gfx::Range& request_range) const;
|
||||
|
||||
// Returns true if there is line break in |range| and stores line breaking
|
||||
// point to |line_breaking_point|. The |line_break_point| is valid only if
|
||||
// this function returns true.
|
||||
static bool GetLineBreakIndex(const std::vector<gfx::Rect>& bounds,
|
||||
const gfx::Range& range,
|
||||
size_t* line_break_point);
|
||||
|
||||
void DestroyNSTextInputOSR();
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
#endif
|
||||
void PlatformCreateCompositorWidget();
|
||||
void PlatformResizeCompositorWidget(const gfx::Size& size);
|
||||
void PlatformDestroyCompositorWidget();
|
||||
|
@ -384,23 +348,6 @@ class CefRenderWidgetHostViewOSR
|
|||
gfx::Vector2dF last_scroll_offset_;
|
||||
bool is_scroll_offset_changed_pending_;
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
NSTextInputContext* text_input_context_osr_mac_;
|
||||
|
||||
// Selected text on the renderer.
|
||||
std::string selected_text_;
|
||||
|
||||
// The current composition character range and its bounds.
|
||||
gfx::Range composition_range_;
|
||||
std::vector<gfx::Rect> composition_bounds_;
|
||||
|
||||
// The current caret bounds.
|
||||
gfx::Rect caret_rect_;
|
||||
|
||||
// The current first selection bounds.
|
||||
gfx::Rect first_selection_rect_;
|
||||
#endif
|
||||
|
||||
base::WeakPtrFactory<CefRenderWidgetHostViewOSR> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CefRenderWidgetHostViewOSR);
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "libcef/browser/browser_host_impl.h"
|
||||
#include "libcef/browser/osr/text_input_client_osr_mac.h"
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
|
@ -20,17 +19,6 @@
|
|||
#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
|
||||
#include "ui/events/latency_info.h"
|
||||
|
||||
namespace {
|
||||
|
||||
CefTextInputClientOSRMac* GetInputClientFromContext(
|
||||
const NSTextInputContext* context) {
|
||||
if (!context)
|
||||
return NULL;
|
||||
return reinterpret_cast<CefTextInputClientOSRMac*>([context client]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
class MacHelper :
|
||||
public content::BrowserCompositorMacClient,
|
||||
|
@ -131,226 +119,6 @@ bool CefRenderWidgetHostViewOSR::IsSpeaking() const {
|
|||
void CefRenderWidgetHostViewOSR::StopSpeaking() {
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::TextInputStateChanged(
|
||||
const content::TextInputState& params) {
|
||||
[NSApp updateWindows];
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeCancelComposition() {
|
||||
CefTextInputClientOSRMac* client = GetInputClientFromContext(
|
||||
text_input_context_osr_mac_);
|
||||
if (client)
|
||||
[client cancelComposition];
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::ImeCompositionRangeChanged(
|
||||
const gfx::Range& range,
|
||||
const std::vector<gfx::Rect>& character_bounds) {
|
||||
CefTextInputClientOSRMac* client = GetInputClientFromContext(
|
||||
text_input_context_osr_mac_);
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
composition_range_ = range;
|
||||
composition_bounds_ = character_bounds;
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::SelectionChanged(
|
||||
const base::string16& text,
|
||||
size_t offset,
|
||||
const gfx::Range& range) {
|
||||
if (range.is_empty() || text.empty()) {
|
||||
selected_text_.clear();
|
||||
} else {
|
||||
size_t pos = range.GetMin() - offset;
|
||||
size_t n = range.length();
|
||||
|
||||
DCHECK(pos + n <= text.length()) << "The text can not fully cover range.";
|
||||
if (pos >= text.length()) {
|
||||
DCHECK(false) << "The text can not cover range.";
|
||||
return;
|
||||
}
|
||||
selected_text_ = base::UTF16ToUTF8(text.substr(pos, n));
|
||||
}
|
||||
|
||||
RenderWidgetHostViewBase::SelectionChanged(text, offset, range);
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::SelectionBoundsChanged(
|
||||
const ViewHostMsg_SelectionBounds_Params& params) {
|
||||
if (params.anchor_rect == params.focus_rect)
|
||||
caret_rect_ = params.anchor_rect;
|
||||
first_selection_rect_ = params.anchor_rect;
|
||||
}
|
||||
|
||||
CefTextInputContext CefRenderWidgetHostViewOSR::GetNSTextInputContext() {
|
||||
if (!text_input_context_osr_mac_) {
|
||||
CefTextInputClientOSRMac* text_input_client_osr_mac =
|
||||
[[CefTextInputClientOSRMac alloc] initWithRenderWidgetHostViewOSR:
|
||||
this];
|
||||
|
||||
text_input_context_osr_mac_ = [[NSTextInputContext alloc] initWithClient:
|
||||
text_input_client_osr_mac];
|
||||
}
|
||||
|
||||
return text_input_context_osr_mac_;
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::HandleKeyEventBeforeTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
CefTextInputClientOSRMac* client = GetInputClientFromContext(
|
||||
text_input_context_osr_mac_);
|
||||
if (client)
|
||||
[client HandleKeyEventBeforeTextInputClient: keyEvent];
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::HandleKeyEventAfterTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
CefTextInputClientOSRMac* client = GetInputClientFromContext(
|
||||
text_input_context_osr_mac_);
|
||||
if (client)
|
||||
[client HandleKeyEventAfterTextInputClient: keyEvent];
|
||||
}
|
||||
|
||||
bool CefRenderWidgetHostViewOSR::GetCachedFirstRectForCharacterRange(
|
||||
gfx::Range range, gfx::Rect* rect, gfx::Range* actual_range) const {
|
||||
DCHECK(rect);
|
||||
|
||||
const gfx::Range requested_range(range);
|
||||
// If requested range is same as caret location, we can just return it.
|
||||
if (selection_range_.is_empty() && requested_range == selection_range_) {
|
||||
if (actual_range)
|
||||
*actual_range = range;
|
||||
*rect = caret_rect_;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (composition_range_.is_empty()) {
|
||||
if (!selection_range_.Contains(requested_range))
|
||||
return false;
|
||||
if (actual_range)
|
||||
*actual_range = selection_range_;
|
||||
*rect = first_selection_rect_;
|
||||
return true;
|
||||
}
|
||||
|
||||
const gfx::Range request_range_in_composition =
|
||||
ConvertCharacterRangeToCompositionRange(requested_range);
|
||||
if (request_range_in_composition == gfx::Range::InvalidRange())
|
||||
return false;
|
||||
|
||||
// If firstRectForCharacterRange in WebFrame is failed in renderer,
|
||||
// ImeCompositionRangeChanged will be sent with empty vector.
|
||||
if (composition_bounds_.empty())
|
||||
return false;
|
||||
DCHECK_EQ(composition_bounds_.size(), composition_range_.length());
|
||||
|
||||
gfx::Range ui_actual_range;
|
||||
*rect = GetFirstRectForCompositionRange(request_range_in_composition,
|
||||
&ui_actual_range);
|
||||
if (actual_range) {
|
||||
*actual_range = gfx::Range(
|
||||
composition_range_.start() + ui_actual_range.start(),
|
||||
composition_range_.start() + ui_actual_range.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gfx::Rect CefRenderWidgetHostViewOSR::GetFirstRectForCompositionRange(
|
||||
const gfx::Range& range, gfx::Range* actual_range) const {
|
||||
DCHECK(actual_range);
|
||||
DCHECK(!composition_bounds_.empty());
|
||||
DCHECK(range.start() <= composition_bounds_.size());
|
||||
DCHECK(range.end() <= composition_bounds_.size());
|
||||
|
||||
if (range.is_empty()) {
|
||||
*actual_range = range;
|
||||
if (range.start() == composition_bounds_.size()) {
|
||||
return gfx::Rect(composition_bounds_[range.start() - 1].right(),
|
||||
composition_bounds_[range.start() - 1].y(),
|
||||
0,
|
||||
composition_bounds_[range.start() - 1].height());
|
||||
} else {
|
||||
return gfx::Rect(composition_bounds_[range.start()].x(),
|
||||
composition_bounds_[range.start()].y(),
|
||||
0,
|
||||
composition_bounds_[range.start()].height());
|
||||
}
|
||||
}
|
||||
|
||||
size_t end_idx;
|
||||
if (!GetLineBreakIndex(composition_bounds_, range, &end_idx)) {
|
||||
end_idx = range.end();
|
||||
}
|
||||
*actual_range = gfx::Range(range.start(), end_idx);
|
||||
gfx::Rect rect = composition_bounds_[range.start()];
|
||||
for (size_t i = range.start() + 1; i < end_idx; ++i) {
|
||||
rect.Union(composition_bounds_[i]);
|
||||
}
|
||||
return rect;
|
||||
}
|
||||
|
||||
gfx::Range CefRenderWidgetHostViewOSR::ConvertCharacterRangeToCompositionRange(
|
||||
const gfx::Range& request_range) const {
|
||||
if (composition_range_.is_empty())
|
||||
return gfx::Range::InvalidRange();
|
||||
|
||||
if (request_range.is_reversed())
|
||||
return gfx::Range::InvalidRange();
|
||||
|
||||
if (request_range.start() < composition_range_.start() ||
|
||||
request_range.start() > composition_range_.end() ||
|
||||
request_range.end() > composition_range_.end()) {
|
||||
return gfx::Range::InvalidRange();
|
||||
}
|
||||
|
||||
return gfx::Range(
|
||||
request_range.start() - composition_range_.start(),
|
||||
request_range.end() - composition_range_.start());
|
||||
}
|
||||
|
||||
bool CefRenderWidgetHostViewOSR::GetLineBreakIndex(
|
||||
const std::vector<gfx::Rect>& bounds,
|
||||
const gfx::Range& range,
|
||||
size_t* line_break_point) {
|
||||
DCHECK(line_break_point);
|
||||
if (range.start() >= bounds.size() || range.is_reversed() || range.is_empty())
|
||||
return false;
|
||||
|
||||
// We can't check line breaking completely from only rectangle array. Thus we
|
||||
// assume the line breaking as the next character's y offset is larger than
|
||||
// a threshold. Currently the threshold is determined as minimum y offset plus
|
||||
// 75% of maximum height.
|
||||
const size_t loop_end_idx =
|
||||
std::min(bounds.size(), static_cast<size_t>(range.end()));
|
||||
int max_height = 0;
|
||||
int min_y_offset = std::numeric_limits<int32_t>::max();
|
||||
for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
|
||||
max_height = std::max(max_height, bounds[idx].height());
|
||||
min_y_offset = std::min(min_y_offset, bounds[idx].y());
|
||||
}
|
||||
int line_break_threshold = min_y_offset + (max_height * 3 / 4);
|
||||
for (size_t idx = range.start(); idx < loop_end_idx; ++idx) {
|
||||
if (bounds[idx].y() > line_break_threshold) {
|
||||
*line_break_point = idx;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CefRenderWidgetHostViewOSR::DestroyNSTextInputOSR() {
|
||||
CefTextInputClientOSRMac* client = GetInputClientFromContext(
|
||||
text_input_context_osr_mac_);
|
||||
if (client) {
|
||||
[client release];
|
||||
client = NULL;
|
||||
}
|
||||
|
||||
[text_input_context_osr_mac_ release];
|
||||
text_input_context_osr_mac_ = NULL;
|
||||
}
|
||||
|
||||
ui::Compositor* CefRenderWidgetHostViewOSR::GetCompositor() const {
|
||||
return browser_compositor_->GetCompositor();
|
||||
}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_LIBCEF_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
|
||||
#define CEF_LIBCEF_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
|
||||
#pragma once
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <vector>
|
||||
|
||||
#include "libcef/browser/osr/render_widget_host_view_osr.h"
|
||||
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h"
|
||||
#include "content/common/edit_command.h"
|
||||
#include "third_party/WebKit/public/web/WebCompositionUnderline.h"
|
||||
|
||||
// Implementation for the NSTextInputClient protocol used for enabling IME on
|
||||
// mac when window rendering is disabled.
|
||||
|
||||
@interface CefTextInputClientOSRMac : NSObject<NSTextInputClient> {
|
||||
@private
|
||||
// Represents the input-method attributes supported by this object.
|
||||
base::scoped_nsobject<NSArray> validAttributesForMarkedText_;
|
||||
|
||||
// Indicates if we are currently handling a key down event.
|
||||
BOOL handlingKeyDown_;
|
||||
|
||||
// Indicates if there is any marked text.
|
||||
BOOL hasMarkedText_;
|
||||
|
||||
// Indicates whether there was any marked text prior to handling
|
||||
// the current key event.
|
||||
BOOL oldHasMarkedText_;
|
||||
|
||||
// Indicates if unmarkText is called or not when handling a keyboard
|
||||
// event.
|
||||
BOOL unmarkTextCalled_;
|
||||
|
||||
// The selected range, cached from a message sent by the renderer.
|
||||
NSRange selectedRange_;
|
||||
|
||||
// Text to be inserted which was generated by handling a key down event.
|
||||
base::string16 textToBeInserted_;
|
||||
|
||||
// Marked text which was generated by handling a key down event.
|
||||
base::string16 markedText_;
|
||||
|
||||
// Underline information of the |markedText_|.
|
||||
std::vector<blink::WebCompositionUnderline> underlines_;
|
||||
|
||||
// Replacement range information received from |setMarkedText:|.
|
||||
gfx::Range setMarkedTextReplacementRange_;
|
||||
|
||||
// Indicates if doCommandBySelector method receives any edit command when
|
||||
// handling a key down event.
|
||||
BOOL hasEditCommands_;
|
||||
|
||||
// Contains edit commands received by the -doCommandBySelector: method when
|
||||
// handling a key down event, not including inserting commands, eg. insertTab,
|
||||
// etc.
|
||||
content::EditCommands editCommands_;
|
||||
|
||||
CefRenderWidgetHostViewOSR* renderWidgetHostView_;
|
||||
}
|
||||
|
||||
@property(nonatomic, readonly) NSRange selectedRange;
|
||||
@property(nonatomic) BOOL handlingKeyDown;
|
||||
|
||||
- (id)initWithRenderWidgetHostViewOSR:(CefRenderWidgetHostViewOSR*) rwhv;
|
||||
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent;
|
||||
- (void)HandleKeyEventAfterTextInputClient:(NSEvent*)keyEvent;
|
||||
- (void)cancelComposition;
|
||||
|
||||
@end
|
||||
|
||||
#endif // CEF_LIBCEF_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
|
|
@ -1,402 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
#include "libcef/browser/osr/text_input_client_osr_mac.h"
|
||||
|
||||
#include "libcef/browser/browser_host_impl.h"
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
|
||||
#import "content/browser/renderer_host/text_input_client_mac.h"
|
||||
#include "content/common/input_messages.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(suzhe): Upstream this function.
|
||||
blink::WebColor WebColorFromNSColor(NSColor *color) {
|
||||
CGFloat r, g, b, a;
|
||||
[color getRed:&r green:&g blue:&b alpha:&a];
|
||||
|
||||
return
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8 |
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
|
||||
}
|
||||
|
||||
// Extract underline information from an attributed string. Mostly copied from
|
||||
// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
|
||||
void ExtractUnderlines(NSAttributedString* string,
|
||||
std::vector<blink::WebCompositionUnderline>* underlines) {
|
||||
int length = [[string string] length];
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
NSRange range;
|
||||
NSDictionary* attrs = [string attributesAtIndex:i
|
||||
longestEffectiveRange:&range
|
||||
inRange:NSMakeRange(i, length - i)];
|
||||
NSNumber *style = [attrs objectForKey: NSUnderlineStyleAttributeName];
|
||||
if (style) {
|
||||
blink::WebColor color = SK_ColorBLACK;
|
||||
if (NSColor *colorAttr =
|
||||
[attrs objectForKey:NSUnderlineColorAttributeName]) {
|
||||
color = WebColorFromNSColor(
|
||||
[colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
|
||||
}
|
||||
underlines->push_back(blink::WebCompositionUnderline(
|
||||
range.location, NSMaxRange(range), color, [style intValue] > 1, 0));
|
||||
}
|
||||
i = range.location + range.length;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
extern NSString* NSTextInputReplacementRangeAttributeName;
|
||||
}
|
||||
|
||||
@implementation CefTextInputClientOSRMac
|
||||
|
||||
@synthesize selectedRange = selectedRange_;
|
||||
@synthesize handlingKeyDown = handlingKeyDown_;
|
||||
|
||||
- (id)initWithRenderWidgetHostViewOSR:(CefRenderWidgetHostViewOSR*)rwhv {
|
||||
self = [super init];
|
||||
renderWidgetHostView_ = rwhv;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray*)validAttributesForMarkedText {
|
||||
if (!validAttributesForMarkedText_) {
|
||||
validAttributesForMarkedText_.reset([[NSArray alloc] initWithObjects:
|
||||
NSUnderlineStyleAttributeName,
|
||||
NSUnderlineColorAttributeName,
|
||||
NSMarkedClauseSegmentAttributeName,
|
||||
NSTextInputReplacementRangeAttributeName,
|
||||
nil]);
|
||||
}
|
||||
return validAttributesForMarkedText_.get();
|
||||
}
|
||||
|
||||
- (NSRange)markedRange {
|
||||
return hasMarkedText_ ?
|
||||
renderWidgetHostView_->composition_range().ToNSRange() :
|
||||
NSMakeRange(NSNotFound, 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText {
|
||||
return hasMarkedText_;
|
||||
}
|
||||
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
|
||||
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
|
||||
NSString* im_text = isAttributedString ? [aString string] : aString;
|
||||
if (handlingKeyDown_) {
|
||||
textToBeInserted_.append(base::SysNSStringToUTF16(im_text));
|
||||
} else {
|
||||
gfx::Range replacement_range(replacementRange);
|
||||
|
||||
renderWidgetHostView_->render_widget_host()->ImeCommitText(
|
||||
base::SysNSStringToUTF16(im_text), replacement_range, 0);
|
||||
}
|
||||
|
||||
// Inserting text will delete all marked text automatically.
|
||||
hasMarkedText_ = NO;
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)aSelector {
|
||||
// An input method calls this function to dispatch an editing command to be
|
||||
// handled by this view.
|
||||
if (aSelector == @selector(noop:))
|
||||
return;
|
||||
std::string command([content::RenderWidgetHostViewMacEditCommandHelper::
|
||||
CommandNameForSelector(aSelector) UTF8String]);
|
||||
|
||||
// If this method is called when handling a key down event, then we need to
|
||||
// handle the command in the key event handler. Otherwise we can just handle
|
||||
// it here.
|
||||
if (handlingKeyDown_) {
|
||||
hasEditCommands_ = YES;
|
||||
// We ignore commands that insert characters, because this was causing
|
||||
// strange behavior (e.g. tab always inserted a tab rather than moving to
|
||||
// the next field on the page).
|
||||
if (!base::StartsWith(command, "insert", base::CompareCase::SENSITIVE))
|
||||
editCommands_.push_back(content::EditCommand(command, ""));
|
||||
} else {
|
||||
renderWidgetHostView_->render_widget_host()->Send(
|
||||
new InputMsg_ExecuteEditCommand(
|
||||
renderWidgetHostView_->render_widget_host()->GetRoutingID(),
|
||||
command, ""));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelRange
|
||||
replacementRange:(NSRange)replacementRange {
|
||||
// An input method updates the composition string.
|
||||
// We send the given text and range to the renderer so it can update the
|
||||
// composition node of WebKit.
|
||||
|
||||
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
|
||||
NSString* im_text = isAttributedString ? [aString string] : aString;
|
||||
int length = [im_text length];
|
||||
|
||||
// |markedRange_| will get set on a callback from ImeSetComposition().
|
||||
selectedRange_ = newSelRange;
|
||||
markedText_ = base::SysNSStringToUTF16(im_text);
|
||||
hasMarkedText_ = (length > 0);
|
||||
underlines_.clear();
|
||||
|
||||
if (isAttributedString) {
|
||||
ExtractUnderlines(aString, &underlines_);
|
||||
} else {
|
||||
// Use a thin black underline by default.
|
||||
underlines_.push_back(blink::WebCompositionUnderline(0, length,
|
||||
SK_ColorBLACK, false, 0));
|
||||
}
|
||||
|
||||
// If we are handling a key down event, then SetComposition() will be
|
||||
// called in keyEvent: method.
|
||||
// Input methods of Mac use setMarkedText calls with an empty text to cancel
|
||||
// an ongoing composition. So, we should check whether or not the given text
|
||||
// is empty to update the input method state. (Our input method backend can
|
||||
// automatically cancels an ongoing composition when we send an empty text.
|
||||
// So, it is OK to send an empty text to the renderer.)
|
||||
if (handlingKeyDown_) {
|
||||
setMarkedTextReplacementRange_ = gfx::Range(replacementRange);
|
||||
} else if (!handlingKeyDown_) {
|
||||
renderWidgetHostView_->render_widget_host()->ImeSetComposition(
|
||||
markedText_, underlines_, gfx::Range(replacementRange),
|
||||
newSelRange.location, NSMaxRange(newSelRange));
|
||||
}
|
||||
}
|
||||
|
||||
- (void)unmarkText {
|
||||
// Delete the composition node of the renderer and finish an ongoing
|
||||
// composition.
|
||||
// It seems an input method calls the setMarkedText method and set an empty
|
||||
// text when it cancels an ongoing composition, i.e. I have never seen an
|
||||
// input method calls this method.
|
||||
hasMarkedText_ = NO;
|
||||
markedText_.clear();
|
||||
underlines_.clear();
|
||||
|
||||
// If we are handling a key down event, then FinishComposingText() will be
|
||||
// called in keyEvent: method.
|
||||
if (!handlingKeyDown_) {
|
||||
renderWidgetHostView_->render_widget_host()->ImeFinishComposingText(false);
|
||||
} else {
|
||||
unmarkTextCalled_ = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
|
||||
actualRange:(NSRangePointer)actualRange {
|
||||
if (actualRange)
|
||||
*actualRange = range;
|
||||
|
||||
const gfx::Range requested_range(range);
|
||||
if (requested_range.is_reversed())
|
||||
return nil;
|
||||
|
||||
gfx::Range expected_range;
|
||||
const base::string16* expected_text;
|
||||
|
||||
if (!renderWidgetHostView_->composition_range().is_empty()) {
|
||||
expected_text = &markedText_;
|
||||
expected_range = renderWidgetHostView_->composition_range();
|
||||
} else {
|
||||
expected_text = &renderWidgetHostView_->selection_text();
|
||||
size_t offset = renderWidgetHostView_->selection_text_offset();
|
||||
expected_range = gfx::Range(offset, offset + expected_text->size());
|
||||
}
|
||||
|
||||
if (!expected_range.Contains(requested_range))
|
||||
return nil;
|
||||
|
||||
// Gets the raw bytes to avoid unnecessary string copies for generating
|
||||
// NSString.
|
||||
const base::char16* bytes =
|
||||
&(*expected_text)[requested_range.start() - expected_range.start()];
|
||||
// Avoid integer overflow.
|
||||
base::CheckedNumeric<size_t> requested_len = requested_range.length();
|
||||
requested_len *= sizeof(base::char16);
|
||||
NSUInteger bytes_len = base::strict_cast<NSUInteger, size_t>(
|
||||
requested_len.ValueOrDefault(0));
|
||||
base::scoped_nsobject<NSString> ns_string(
|
||||
[[NSString alloc] initWithBytes:bytes
|
||||
length:bytes_len
|
||||
encoding:NSUTF16LittleEndianStringEncoding]);
|
||||
return [[[NSAttributedString alloc] initWithString:ns_string] autorelease];
|
||||
}
|
||||
|
||||
- (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
|
||||
actualRange:(NSRangePointer)actualRange {
|
||||
NSRect rect;
|
||||
gfx::Rect gfxRect;
|
||||
gfx::Range range(theRange);
|
||||
gfx::Range actual_range;
|
||||
if (!renderWidgetHostView_->GetCachedFirstRectForCharacterRange(range,
|
||||
&gfxRect, &actual_range)) {
|
||||
rect = content::TextInputClientMac::GetInstance()->
|
||||
GetFirstRectForRange(renderWidgetHostView_->GetRenderWidgetHost(),
|
||||
range.ToNSRange());
|
||||
|
||||
if (actualRange)
|
||||
*actualRange = range.ToNSRange();
|
||||
} else {
|
||||
rect = NSRectFromCGRect(gfxRect.ToCGRect());
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (NSRect) screenRectFromViewRect:(NSRect)rect {
|
||||
NSRect screenRect;
|
||||
|
||||
int screenX, screenY;
|
||||
renderWidgetHostView_->browser_impl()->GetClient()->GetRenderHandler()->
|
||||
GetScreenPoint(renderWidgetHostView_->browser_impl()->GetBrowser(),
|
||||
rect.origin.x, rect.origin.y, screenX, screenY);
|
||||
screenRect.origin = NSMakePoint(screenX, screenY);
|
||||
screenRect.size = rect.size;
|
||||
|
||||
return screenRect;
|
||||
}
|
||||
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
|
||||
actualRange:(NSRangePointer)actualRange {
|
||||
NSRect rect = [self firstViewRectForCharacterRange:theRange
|
||||
actualRange:actualRange];
|
||||
|
||||
// Convert into screen coordinates for return.
|
||||
rect = [self screenRectFromViewRect:rect];
|
||||
|
||||
if (rect.origin.y >= rect.size.height)
|
||||
rect.origin.y -= rect.size.height;
|
||||
else
|
||||
rect.origin.y = 0;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
|
||||
// |thePoint| is in screen coordinates, but needs to be converted to WebKit
|
||||
// coordinates (upper left origin). Scroll offsets will be taken care of in
|
||||
// the renderer.
|
||||
|
||||
CefRect view_rect;
|
||||
renderWidgetHostView_->browser_impl()->GetClient()->GetRenderHandler()->
|
||||
GetViewRect(renderWidgetHostView_->browser_impl()->GetBrowser(),
|
||||
view_rect);
|
||||
|
||||
thePoint.x -= view_rect.x;
|
||||
thePoint.y -= view_rect.y;
|
||||
thePoint.y = view_rect.height - thePoint.y;
|
||||
|
||||
NSUInteger index = content::TextInputClientMac::GetInstance()->
|
||||
GetCharacterIndexAtPoint(renderWidgetHostView_->GetRenderWidgetHost(),
|
||||
gfx::Point(thePoint.x, thePoint.y));
|
||||
return index;
|
||||
}
|
||||
|
||||
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent {
|
||||
DCHECK([keyEvent type] == NSKeyDown);
|
||||
// Don't call this method recursively.
|
||||
DCHECK(!handlingKeyDown_);
|
||||
|
||||
oldHasMarkedText_ = hasMarkedText_;
|
||||
handlingKeyDown_ = YES;
|
||||
|
||||
// These variables might be set when handling the keyboard event.
|
||||
// Clear them here so that we can know whether they have changed afterwards.
|
||||
textToBeInserted_.clear();
|
||||
markedText_.clear();
|
||||
underlines_.clear();
|
||||
setMarkedTextReplacementRange_ = gfx::Range::InvalidRange();
|
||||
unmarkTextCalled_ = NO;
|
||||
hasEditCommands_ = NO;
|
||||
editCommands_.clear();
|
||||
}
|
||||
|
||||
- (void)HandleKeyEventAfterTextInputClient:(NSEvent*)keyEvent {
|
||||
handlingKeyDown_ = NO;
|
||||
|
||||
// Then send keypress and/or composition related events.
|
||||
// If there was a marked text or the text to be inserted is longer than 1
|
||||
// character, then we send the text by calling FinishComposingText().
|
||||
// Otherwise, if the text to be inserted only contains 1 character, then we
|
||||
// can just send a keypress event which is fabricated by changing the type of
|
||||
// the keydown event, so that we can retain all necessary informations, such
|
||||
// as unmodifiedText, etc. And we need to set event.skip_in_browser to true to
|
||||
// prevent the browser from handling it again.
|
||||
// Note that, |textToBeInserted_| is a UTF-16 string, but it's fine to only
|
||||
// handle BMP characters here, as we can always insert non-BMP characters as
|
||||
// text.
|
||||
|
||||
if (!hasMarkedText_ && !oldHasMarkedText_ &&
|
||||
textToBeInserted_.length() <= 1) {
|
||||
content::NativeWebKeyboardEvent event(keyEvent);
|
||||
if (textToBeInserted_.length() == 1) {
|
||||
event.type = blink::WebInputEvent::Type::Char;
|
||||
event.text[0] = textToBeInserted_[0];
|
||||
event.text[1] = 0;
|
||||
}
|
||||
renderWidgetHostView_->SendKeyEvent(event);
|
||||
}
|
||||
|
||||
BOOL textInserted = NO;
|
||||
if (textToBeInserted_.length() >
|
||||
((hasMarkedText_ || oldHasMarkedText_) ? 0u : 1u)) {
|
||||
renderWidgetHostView_->render_widget_host()->ImeCommitText(
|
||||
textToBeInserted_, gfx::Range::InvalidRange(), 0);
|
||||
textToBeInserted_ = YES;
|
||||
}
|
||||
|
||||
// Updates or cancels the composition. If some text has been inserted, then
|
||||
// we don't need to cancel the composition explicitly.
|
||||
if (hasMarkedText_ && markedText_.length()) {
|
||||
// Sends the updated marked text to the renderer so it can update the
|
||||
// composition node in WebKit.
|
||||
// When marked text is available, |selectedRange_| will be the range being
|
||||
// selected inside the marked text.
|
||||
renderWidgetHostView_->render_widget_host()->ImeSetComposition(
|
||||
markedText_, underlines_, setMarkedTextReplacementRange_,
|
||||
selectedRange_.location, NSMaxRange(selectedRange_));
|
||||
} else if (oldHasMarkedText_ && !hasMarkedText_ && !textInserted) {
|
||||
if (unmarkTextCalled_) {
|
||||
renderWidgetHostView_->render_widget_host()->ImeFinishComposingText(
|
||||
false);
|
||||
} else {
|
||||
renderWidgetHostView_->render_widget_host()->ImeCancelComposition();
|
||||
}
|
||||
}
|
||||
|
||||
setMarkedTextReplacementRange_ = gfx::Range::InvalidRange();
|
||||
}
|
||||
|
||||
- (void)cancelComposition {
|
||||
if (!hasMarkedText_)
|
||||
return;
|
||||
|
||||
// Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
|
||||
// doesn't call any NSTextInput functions, such as setMarkedText or
|
||||
// insertText. So, we need to send an IPC message to a renderer so it can
|
||||
// delete the composition node.
|
||||
// TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
|
||||
// NSTextInputContext. http://www.crbug.com/479010.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
|
||||
[currentInputManager markedTextAbandoned:self];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
hasMarkedText_ = NO;
|
||||
// Should not call [self unmarkText] here, because it'll send unnecessary
|
||||
// cancel composition IPC message to the renderer.
|
||||
}
|
||||
|
||||
@end
|
|
@ -781,46 +781,96 @@ void CEF_CALLBACK browser_host_set_windowless_frame_rate(
|
|||
frame_rate);
|
||||
}
|
||||
|
||||
cef_text_input_context_t CEF_CALLBACK browser_host_get_nstext_input_context(
|
||||
void CEF_CALLBACK browser_host_ime_set_composition(
|
||||
struct _cef_browser_host_t* self, const cef_string_t* text,
|
||||
size_t underlinesCount, cef_composition_underline_t const* underlines,
|
||||
const cef_range_t* replacement_range,
|
||||
const cef_range_t* selection_range) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
// Verify param: replacement_range; type: simple_byref_const
|
||||
DCHECK(replacement_range);
|
||||
if (!replacement_range)
|
||||
return;
|
||||
// Verify param: selection_range; type: simple_byref_const
|
||||
DCHECK(selection_range);
|
||||
if (!selection_range)
|
||||
return;
|
||||
// Unverified params: text, underlines
|
||||
|
||||
// Translate param: underlines; type: simple_vec_byref_const
|
||||
std::vector<CefCompositionUnderline > underlinesList;
|
||||
if (underlinesCount > 0) {
|
||||
for (size_t i = 0; i < underlinesCount; ++i) {
|
||||
CefCompositionUnderline underlinesVal = underlines[i];
|
||||
underlinesList.push_back(underlinesVal);
|
||||
}
|
||||
}
|
||||
// Translate param: replacement_range; type: simple_byref_const
|
||||
CefRange replacement_rangeVal = replacement_range?*replacement_range:CefRange(
|
||||
);
|
||||
// Translate param: selection_range; type: simple_byref_const
|
||||
CefRange selection_rangeVal = selection_range?*selection_range:CefRange();
|
||||
|
||||
// Execute
|
||||
CefBrowserHostCppToC::Get(self)->ImeSetComposition(
|
||||
CefString(text),
|
||||
underlinesList,
|
||||
replacement_rangeVal,
|
||||
selection_rangeVal);
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_ime_commit_text(struct _cef_browser_host_t* self,
|
||||
const cef_string_t* text, const cef_range_t* replacement_range,
|
||||
int relative_cursor_pos) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
// Verify param: replacement_range; type: simple_byref_const
|
||||
DCHECK(replacement_range);
|
||||
if (!replacement_range)
|
||||
return;
|
||||
// Unverified params: text
|
||||
|
||||
// Translate param: replacement_range; type: simple_byref_const
|
||||
CefRange replacement_rangeVal = replacement_range?*replacement_range:CefRange(
|
||||
);
|
||||
|
||||
// Execute
|
||||
CefBrowserHostCppToC::Get(self)->ImeCommitText(
|
||||
CefString(text),
|
||||
replacement_rangeVal,
|
||||
relative_cursor_pos);
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_ime_finish_composing_text(
|
||||
struct _cef_browser_host_t* self, int keep_selection) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
// Execute
|
||||
CefBrowserHostCppToC::Get(self)->ImeFinishComposingText(
|
||||
keep_selection?true:false);
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_ime_cancel_composition(
|
||||
struct _cef_browser_host_t* self) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
// Execute
|
||||
cef_text_input_context_t _retval = CefBrowserHostCppToC::Get(
|
||||
self)->GetNSTextInputContext();
|
||||
|
||||
// Return type: simple
|
||||
return _retval;
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_handle_key_event_before_text_input_client(
|
||||
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
// Execute
|
||||
CefBrowserHostCppToC::Get(self)->HandleKeyEventBeforeTextInputClient(
|
||||
keyEvent);
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_handle_key_event_after_text_input_client(
|
||||
struct _cef_browser_host_t* self, cef_event_handle_t keyEvent) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
// Execute
|
||||
CefBrowserHostCppToC::Get(self)->HandleKeyEventAfterTextInputClient(
|
||||
keyEvent);
|
||||
CefBrowserHostCppToC::Get(self)->ImeCancelComposition();
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_drag_target_drag_enter(
|
||||
|
@ -1008,11 +1058,11 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() {
|
|||
browser_host_get_windowless_frame_rate;
|
||||
GetStruct()->set_windowless_frame_rate =
|
||||
browser_host_set_windowless_frame_rate;
|
||||
GetStruct()->get_nstext_input_context = browser_host_get_nstext_input_context;
|
||||
GetStruct()->handle_key_event_before_text_input_client =
|
||||
browser_host_handle_key_event_before_text_input_client;
|
||||
GetStruct()->handle_key_event_after_text_input_client =
|
||||
browser_host_handle_key_event_after_text_input_client;
|
||||
GetStruct()->ime_set_composition = browser_host_ime_set_composition;
|
||||
GetStruct()->ime_commit_text = browser_host_ime_commit_text;
|
||||
GetStruct()->ime_finish_composing_text =
|
||||
browser_host_ime_finish_composing_text;
|
||||
GetStruct()->ime_cancel_composition = browser_host_ime_cancel_composition;
|
||||
GetStruct()->drag_target_drag_enter = browser_host_drag_target_drag_enter;
|
||||
GetStruct()->drag_target_drag_over = browser_host_drag_target_drag_over;
|
||||
GetStruct()->drag_target_drag_leave = browser_host_drag_target_drag_leave;
|
||||
|
|
|
@ -350,6 +350,46 @@ void CEF_CALLBACK render_handler_on_scroll_offset_changed(
|
|||
y);
|
||||
}
|
||||
|
||||
void CEF_CALLBACK render_handler_on_ime_composition_range_changed(
|
||||
struct _cef_render_handler_t* self, cef_browser_t* browser,
|
||||
const cef_range_t* selected_range, size_t character_boundsCount,
|
||||
cef_rect_t const* character_bounds) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
// Verify param: browser; type: refptr_diff
|
||||
DCHECK(browser);
|
||||
if (!browser)
|
||||
return;
|
||||
// Verify param: selected_range; type: simple_byref_const
|
||||
DCHECK(selected_range);
|
||||
if (!selected_range)
|
||||
return;
|
||||
// Verify param: character_bounds; type: simple_vec_byref_const
|
||||
DCHECK(character_boundsCount == 0 || character_bounds);
|
||||
if (character_boundsCount > 0 && !character_bounds)
|
||||
return;
|
||||
|
||||
// Translate param: selected_range; type: simple_byref_const
|
||||
CefRange selected_rangeVal = selected_range?*selected_range:CefRange();
|
||||
// Translate param: character_bounds; type: simple_vec_byref_const
|
||||
std::vector<CefRect > character_boundsList;
|
||||
if (character_boundsCount > 0) {
|
||||
for (size_t i = 0; i < character_boundsCount; ++i) {
|
||||
CefRect character_boundsVal = character_bounds[i];
|
||||
character_boundsList.push_back(character_boundsVal);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute
|
||||
CefRenderHandlerCppToC::Get(self)->OnImeCompositionRangeChanged(
|
||||
CefBrowserCToCpp::Wrap(browser),
|
||||
selected_rangeVal,
|
||||
character_boundsList);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
@ -368,6 +408,8 @@ CefRenderHandlerCppToC::CefRenderHandlerCppToC() {
|
|||
GetStruct()->update_drag_cursor = render_handler_update_drag_cursor;
|
||||
GetStruct()->on_scroll_offset_changed =
|
||||
render_handler_on_scroll_offset_changed;
|
||||
GetStruct()->on_ime_composition_range_changed =
|
||||
render_handler_on_ime_composition_range_changed;
|
||||
}
|
||||
|
||||
template<> CefRefPtr<CefRenderHandler> CefCppToC<CefRenderHandlerCppToC,
|
||||
|
|
|
@ -667,44 +667,81 @@ void CefBrowserHostCToCpp::SetWindowlessFrameRate(int frame_rate) {
|
|||
frame_rate);
|
||||
}
|
||||
|
||||
CefTextInputContext CefBrowserHostCToCpp::GetNSTextInputContext() {
|
||||
void CefBrowserHostCToCpp::ImeSetComposition(const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range, const CefRange& selection_range) {
|
||||
cef_browser_host_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, get_nstext_input_context))
|
||||
return NULL;
|
||||
if (CEF_MEMBER_MISSING(_struct, ime_set_composition))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Execute
|
||||
cef_text_input_context_t _retval = _struct->get_nstext_input_context(_struct);
|
||||
// Unverified params: text, underlines
|
||||
|
||||
// Return type: simple
|
||||
return _retval;
|
||||
// Translate param: underlines; type: simple_vec_byref_const
|
||||
const size_t underlinesCount = underlines.size();
|
||||
cef_composition_underline_t* underlinesList = NULL;
|
||||
if (underlinesCount > 0) {
|
||||
underlinesList = new cef_composition_underline_t[underlinesCount];
|
||||
DCHECK(underlinesList);
|
||||
if (underlinesList) {
|
||||
for (size_t i = 0; i < underlinesCount; ++i) {
|
||||
underlinesList[i] = underlines[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute
|
||||
_struct->ime_set_composition(_struct,
|
||||
text.GetStruct(),
|
||||
underlinesCount,
|
||||
underlinesList,
|
||||
&replacement_range,
|
||||
&selection_range);
|
||||
|
||||
// Restore param:underlines; type: simple_vec_byref_const
|
||||
if (underlinesList)
|
||||
delete [] underlinesList;
|
||||
}
|
||||
|
||||
void CefBrowserHostCToCpp::HandleKeyEventBeforeTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
void CefBrowserHostCToCpp::ImeCommitText(const CefString& text,
|
||||
const CefRange& replacement_range, int relative_cursor_pos) {
|
||||
cef_browser_host_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, handle_key_event_before_text_input_client))
|
||||
if (CEF_MEMBER_MISSING(_struct, ime_commit_text))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Unverified params: text
|
||||
|
||||
// Execute
|
||||
_struct->ime_commit_text(_struct,
|
||||
text.GetStruct(),
|
||||
&replacement_range,
|
||||
relative_cursor_pos);
|
||||
}
|
||||
|
||||
void CefBrowserHostCToCpp::ImeFinishComposingText(bool keep_selection) {
|
||||
cef_browser_host_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, ime_finish_composing_text))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Execute
|
||||
_struct->handle_key_event_before_text_input_client(_struct,
|
||||
keyEvent);
|
||||
_struct->ime_finish_composing_text(_struct,
|
||||
keep_selection);
|
||||
}
|
||||
|
||||
void CefBrowserHostCToCpp::HandleKeyEventAfterTextInputClient(
|
||||
CefEventHandle keyEvent) {
|
||||
void CefBrowserHostCToCpp::ImeCancelComposition() {
|
||||
cef_browser_host_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, handle_key_event_after_text_input_client))
|
||||
if (CEF_MEMBER_MISSING(_struct, ime_cancel_composition))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Execute
|
||||
_struct->handle_key_event_after_text_input_client(_struct,
|
||||
keyEvent);
|
||||
_struct->ime_cancel_composition(_struct);
|
||||
}
|
||||
|
||||
void CefBrowserHostCToCpp::DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
|
||||
|
|
|
@ -86,9 +86,14 @@ class CefBrowserHostCToCpp
|
|||
void NotifyMoveOrResizeStarted() OVERRIDE;
|
||||
int GetWindowlessFrameRate() OVERRIDE;
|
||||
void SetWindowlessFrameRate(int frame_rate) OVERRIDE;
|
||||
CefTextInputContext GetNSTextInputContext() OVERRIDE;
|
||||
void HandleKeyEventBeforeTextInputClient(CefEventHandle keyEvent) OVERRIDE;
|
||||
void HandleKeyEventAfterTextInputClient(CefEventHandle keyEvent) OVERRIDE;
|
||||
void ImeSetComposition(const CefString& text,
|
||||
const std::vector<CefCompositionUnderline>& underlines,
|
||||
const CefRange& replacement_range,
|
||||
const CefRange& selection_range) OVERRIDE;
|
||||
void ImeCommitText(const CefString& text, const CefRange& replacement_range,
|
||||
int relative_cursor_pos) OVERRIDE;
|
||||
void ImeFinishComposingText(bool keep_selection) OVERRIDE;
|
||||
void ImeCancelComposition() OVERRIDE;
|
||||
void DragTargetDragEnter(CefRefPtr<CefDragData> drag_data,
|
||||
const CefMouseEvent& event, DragOperationsMask allowed_ops) OVERRIDE;
|
||||
void DragTargetDragOver(const CefMouseEvent& event,
|
||||
|
|
|
@ -283,6 +283,45 @@ void CefRenderHandlerCToCpp::OnScrollOffsetChanged(
|
|||
y);
|
||||
}
|
||||
|
||||
void CefRenderHandlerCToCpp::OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser, const CefRange& selected_range,
|
||||
const RectList& character_bounds) {
|
||||
cef_render_handler_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, on_ime_composition_range_changed))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Verify param: browser; type: refptr_diff
|
||||
DCHECK(browser.get());
|
||||
if (!browser.get())
|
||||
return;
|
||||
|
||||
// Translate param: character_bounds; type: simple_vec_byref_const
|
||||
const size_t character_boundsCount = character_bounds.size();
|
||||
cef_rect_t* character_boundsList = NULL;
|
||||
if (character_boundsCount > 0) {
|
||||
character_boundsList = new cef_rect_t[character_boundsCount];
|
||||
DCHECK(character_boundsList);
|
||||
if (character_boundsList) {
|
||||
for (size_t i = 0; i < character_boundsCount; ++i) {
|
||||
character_boundsList[i] = character_bounds[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute
|
||||
_struct->on_ime_composition_range_changed(_struct,
|
||||
CefBrowserCppToC::Wrap(browser),
|
||||
&selected_range,
|
||||
character_boundsCount,
|
||||
character_boundsList);
|
||||
|
||||
// Restore param:character_bounds; type: simple_vec_byref_const
|
||||
if (character_boundsList)
|
||||
delete [] character_boundsList;
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR - Do not edit by hand.
|
||||
|
||||
|
|
|
@ -51,6 +51,9 @@ class CefRenderHandlerCToCpp
|
|||
DragOperation operation) override;
|
||||
void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser, double x,
|
||||
double y) override;
|
||||
void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selected_range,
|
||||
const RectList& character_bounds) override;
|
||||
};
|
||||
|
||||
#endif // BUILDING_CEF_SHARED
|
||||
|
|
|
@ -1204,6 +1204,13 @@ void BrowserWindowOsrGtk::UpdateDragCursor(
|
|||
CEF_REQUIRE_UI_THREAD();
|
||||
}
|
||||
|
||||
void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
}
|
||||
|
||||
void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
|
||||
REQUIRE_MAIN_THREAD();
|
||||
DCHECK(!glarea_);
|
||||
|
|
|
@ -76,6 +76,10 @@ class BrowserWindowOsrGtk : public BrowserWindow,
|
|||
int x, int y) OVERRIDE;
|
||||
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
|
||||
CefRenderHandler::DragOperation operation) OVERRIDE;
|
||||
void OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Create the GTK GlArea.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "cefclient/browser/browser_window.h"
|
||||
#include "cefclient/browser/client_handler_osr.h"
|
||||
#include "cefclient/browser/osr_renderer.h"
|
||||
#include "cefclient/browser/text_input_client_osr_mac.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
|
@ -77,6 +78,10 @@ class BrowserWindowOsrMac : public BrowserWindow,
|
|||
int x, int y) OVERRIDE;
|
||||
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
|
||||
CefRenderHandler::DragOperation operation) OVERRIDE;
|
||||
void OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Create the NSView.
|
||||
|
|
|
@ -14,6 +14,18 @@
|
|||
#include "cefclient/browser/bytes_write_handler.h"
|
||||
#include "cefclient/browser/geometry_util.h"
|
||||
#include "cefclient/browser/main_message_loop.h"
|
||||
#include "cefclient/browser/text_input_client_osr_mac.h"
|
||||
|
||||
namespace {
|
||||
|
||||
CefTextInputClientOSRMac* GetInputClientFromContext(
|
||||
const NSTextInputContext* context) {
|
||||
if (!context)
|
||||
return NULL;
|
||||
return reinterpret_cast<CefTextInputClientOSRMac*>([context client]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@interface BrowserOpenGLView
|
||||
: NSOpenGLView <NSDraggingSource, NSDraggingDestination> {
|
||||
|
@ -29,13 +41,16 @@
|
|||
|
||||
float device_scale_factor_;
|
||||
|
||||
// Drag and drop
|
||||
// Drag and drop.
|
||||
CefRefPtr<CefDragData> current_drag_data_;
|
||||
NSDragOperation current_drag_op_;
|
||||
NSDragOperation current_allowed_ops_;
|
||||
NSPasteboard* pasteboard_;
|
||||
CFStringRef fileUTI_;
|
||||
|
||||
// For intreacting with IME.
|
||||
NSTextInputContext* text_input_context_osr_mac_;
|
||||
|
||||
// Event monitor for scroll wheel end event.
|
||||
id endWheelMonitor_;
|
||||
}
|
||||
|
@ -81,7 +96,8 @@
|
|||
- (NSPoint)convertPointToBackingInternal:(NSPoint)aPoint;
|
||||
- (NSRect)convertRectFromBackingInternal:(NSRect)aRect;
|
||||
- (NSRect)convertRectToBackingInternal:(NSRect)aRect;
|
||||
|
||||
- (void)ChangeCompositionRange:(CefRange)range
|
||||
character_bounds:(const CefRenderHandler::RectList&) character_bounds;
|
||||
@end
|
||||
|
||||
|
||||
|
@ -315,18 +331,25 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
|||
|
||||
- (void)keyDown:(NSEvent*)event {
|
||||
CefRefPtr<CefBrowser> browser = [self getBrowser];
|
||||
if (!browser.get())
|
||||
if (!browser.get() || !text_input_context_osr_mac_)
|
||||
return;
|
||||
|
||||
if ([event type] != NSFlagsChanged) {
|
||||
browser->GetHost()->HandleKeyEventBeforeTextInputClient(event);
|
||||
CefTextInputClientOSRMac* client = GetInputClientFromContext(
|
||||
text_input_context_osr_mac_);
|
||||
|
||||
// The return value of this method seems to always be set to YES,
|
||||
// thus we ignore it and ask the host view whether IME is active
|
||||
// or not.
|
||||
[[self inputContext] handleEvent:event];
|
||||
if (client) {
|
||||
[client HandleKeyEventBeforeTextInputClient:event];
|
||||
|
||||
browser->GetHost()->HandleKeyEventAfterTextInputClient(event);
|
||||
// The return value of this method seems to always be set to YES, thus we
|
||||
// ignore it and ask the host view whether IME is active or not.
|
||||
[text_input_context_osr_mac_ handleEvent:event];
|
||||
|
||||
CefKeyEvent keyEvent;
|
||||
[self getKeyEvent:keyEvent forEvent:event];
|
||||
|
||||
[client HandleKeyEventAfterTextInputClient:keyEvent];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -502,10 +525,15 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
|||
}
|
||||
|
||||
- (NSTextInputContext*)inputContext {
|
||||
CefRefPtr<CefBrowser> browser = [self getBrowser];
|
||||
if (browser.get())
|
||||
return browser->GetHost()->GetNSTextInputContext();
|
||||
return NULL;
|
||||
if (!text_input_context_osr_mac_) {
|
||||
CefTextInputClientOSRMac* text_input_client =
|
||||
[[CefTextInputClientOSRMac alloc] initWithBrowser:[self getBrowser]];
|
||||
|
||||
text_input_context_osr_mac_ = [[NSTextInputContext alloc] initWithClient:
|
||||
text_input_client];
|
||||
}
|
||||
|
||||
return text_input_context_osr_mac_;
|
||||
}
|
||||
|
||||
- (void)getMouseEvent:(CefMouseEvent&)mouseEvent forEvent:(NSEvent*)event {
|
||||
|
@ -1098,6 +1126,13 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
|||
return [self convertRectToBacking:aRect];
|
||||
}
|
||||
|
||||
- (void)ChangeCompositionRange:(CefRange)range
|
||||
character_bounds:(const CefRenderHandler::RectList&) bounds {
|
||||
CefTextInputClientOSRMac* client =
|
||||
GetInputClientFromContext(text_input_context_osr_mac_);
|
||||
if (client)
|
||||
[client ChangeCompositionRange: range character_bounds:bounds];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
@ -1437,6 +1472,18 @@ void BrowserWindowOsrMac::UpdateDragCursor(
|
|||
[GLView(nsview_) setCurrentDragOp:operation];
|
||||
}
|
||||
|
||||
void BrowserWindowOsrMac::OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& bounds) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
if (nsview_) {
|
||||
[GLView(nsview_) ChangeCompositionRange:selection_range
|
||||
character_bounds:bounds];
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserWindowOsrMac::Create(ClientWindowHandle parent_handle,
|
||||
const CefRect& rect) {
|
||||
REQUIRE_MAIN_THREAD();
|
||||
|
|
|
@ -135,4 +135,15 @@ void ClientHandlerOsr::UpdateDragCursor(CefRefPtr<CefBrowser> browser,
|
|||
osr_delegate_->UpdateDragCursor(browser, operation);
|
||||
}
|
||||
|
||||
void ClientHandlerOsr::OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
if (!osr_delegate_)
|
||||
return;
|
||||
osr_delegate_->OnImeCompositionRangeChanged(browser, selection_range,
|
||||
character_bounds);
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
|
|
@ -56,6 +56,10 @@ class ClientHandlerOsr : public ClientHandler,
|
|||
virtual void UpdateDragCursor(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
CefRenderHandler::DragOperation operation) = 0;
|
||||
virtual void OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~OsrDelegate() {}
|
||||
|
@ -109,6 +113,10 @@ class ClientHandlerOsr : public ClientHandler,
|
|||
int x, int y) OVERRIDE;
|
||||
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
|
||||
CefRenderHandler::DragOperation operation) OVERRIDE;
|
||||
void OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Only accessed on the UI thread.
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Implementation based on ui/base/ime/win/imm32_manager.cc from Chromium.
|
||||
|
||||
#include <windowsx.h>
|
||||
#include <msctf.h>
|
||||
|
||||
#include "include/base/cef_build.h"
|
||||
#include "cefclient/browser/geometry_util.h"
|
||||
#include "cefclient/browser/main_message_loop.h"
|
||||
#include "cefclient/browser/resource.h"
|
||||
#include "cefclient/browser/util_win.h"
|
||||
#include "cefclient/browser/osr_ime_handler_win.h"
|
||||
|
||||
#define ColorUNDERLINE 0xFF000000 // Black SkColor value for underline,
|
||||
// same as Blink.
|
||||
#define ColorBKCOLOR 0x00000000 // White SkColor value for background,
|
||||
// same as Blink.
|
||||
|
||||
namespace client {
|
||||
|
||||
namespace {
|
||||
|
||||
// Determines whether or not the given attribute represents a selection
|
||||
bool IsSelectionAttribute(char attribute) {
|
||||
return (attribute == ATTR_TARGET_CONVERTED ||
|
||||
attribute == ATTR_TARGET_NOTCONVERTED);
|
||||
}
|
||||
|
||||
// Helper function for OsrImeHandlerWin::GetCompositionInfo() method,
|
||||
// to get the target range that's selected by the user in the current
|
||||
// composition string.
|
||||
void GetCompositionSelectionRange(HIMC imc, int* target_start,
|
||||
int* target_end) {
|
||||
int attribute_size = ::ImmGetCompositionString(imc, GCS_COMPATTR, NULL, 0);
|
||||
if (attribute_size > 0) {
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
std::vector<char> attribute_data(attribute_size);
|
||||
|
||||
::ImmGetCompositionString(imc, GCS_COMPATTR, &attribute_data[0],
|
||||
attribute_size);
|
||||
for (start = 0; start < attribute_size; ++start) {
|
||||
if (IsSelectionAttribute(attribute_data[start]))
|
||||
break;
|
||||
}
|
||||
for (end = start; end < attribute_size; ++end) {
|
||||
if (!IsSelectionAttribute(attribute_data[end]))
|
||||
break;
|
||||
}
|
||||
|
||||
*target_start = start;
|
||||
*target_end = end;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function for OsrImeHandlerWin::GetCompositionInfo() method, to get
|
||||
// underlines information of the current composition string.
|
||||
void GetCompositionUnderlines(
|
||||
HIMC imc,
|
||||
int target_start,
|
||||
int target_end,
|
||||
std::vector<CefCompositionUnderline> &underlines) {
|
||||
int clause_size = ::ImmGetCompositionString(imc, GCS_COMPCLAUSE, NULL, 0);
|
||||
int clause_length = clause_size / sizeof(uint32);
|
||||
if (clause_length) {
|
||||
std::vector<uint32> clause_data(clause_length);
|
||||
|
||||
::ImmGetCompositionString(imc, GCS_COMPCLAUSE,
|
||||
&clause_data[0], clause_size);
|
||||
for (int i = 0; i < clause_length - 1; ++i) {
|
||||
cef_composition_underline_t underline;
|
||||
underline.range.from = clause_data[i];
|
||||
underline.range.to = clause_data[i + 1];
|
||||
underline.color = ColorUNDERLINE;
|
||||
underline.background_color = ColorBKCOLOR;
|
||||
underline.thick = 0;
|
||||
|
||||
// Use thick underline for the target clause.
|
||||
if (underline.range.from >= target_start &&
|
||||
underline.range.to <= target_end) {
|
||||
underline.thick = 1;
|
||||
}
|
||||
underlines.push_back(underline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OsrImeHandlerWin::OsrImeHandlerWin(HWND hwnd)
|
||||
: ime_status_(false),
|
||||
hwnd_(hwnd),
|
||||
input_language_id_(LANG_USER_DEFAULT),
|
||||
is_composing_(false),
|
||||
cursor_index_(-1),
|
||||
system_caret_(false) {
|
||||
ime_rect_ = { -1, -1, 0, 0 };
|
||||
}
|
||||
|
||||
OsrImeHandlerWin::~OsrImeHandlerWin() {
|
||||
DestroyImeWindow();
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::SetInputLanguage() {
|
||||
// Retrieve the current input language from the system's keyboard layout.
|
||||
// Using GetKeyboardLayoutName instead of GetKeyboardLayout, because
|
||||
// the language from GetKeyboardLayout is the language under where the
|
||||
// keyboard layout is installed. And the language from GetKeyboardLayoutName
|
||||
// indicates the language of the keyboard layout itself.
|
||||
// See crbug.com/344834.
|
||||
WCHAR keyboard_layout[KL_NAMELENGTH];
|
||||
if (::GetKeyboardLayoutNameW(keyboard_layout)) {
|
||||
input_language_id_ =
|
||||
static_cast<LANGID>(_wtoi(&keyboard_layout[KL_NAMELENGTH >> 1]));
|
||||
} else {
|
||||
input_language_id_ = 0x0409; // Fallback to en-US.
|
||||
}
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::CreateImeWindow() {
|
||||
// Chinese/Japanese IMEs somehow ignore function calls to
|
||||
// ::ImmSetCandidateWindow(), and use the position of the current system
|
||||
// caret instead -::GetCaretPos().
|
||||
// Therefore, we create a temporary system caret for Chinese IMEs and use
|
||||
// it during this input context.
|
||||
// Since some third-party Japanese IME also uses ::GetCaretPos() to determine
|
||||
// their window position, we also create a caret for Japanese IMEs.
|
||||
if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE ||
|
||||
PRIMARYLANGID(input_language_id_) == LANG_JAPANESE) {
|
||||
if (!system_caret_) {
|
||||
if (::CreateCaret(hwnd_, NULL, 1, 1))
|
||||
system_caret_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::DestroyImeWindow() {
|
||||
// Destroy the system caret if we have created for this IME input context.
|
||||
if (system_caret_) {
|
||||
::DestroyCaret();
|
||||
system_caret_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::MoveImeWindow() {
|
||||
// Does nothing when the target window has no input focus.
|
||||
if (GetFocus() != hwnd_)
|
||||
return;
|
||||
|
||||
CefRect rc = ime_rect_;
|
||||
int location = cursor_index_;
|
||||
|
||||
// If location is not specified fall back to the composition range start.
|
||||
if (location == -1)
|
||||
location = composition_range_.from;
|
||||
|
||||
// Offset location by the composition range start if required.
|
||||
if (location >= composition_range_.from)
|
||||
location -= composition_range_.from;
|
||||
|
||||
if (location < static_cast<int>(composition_bounds_.size()))
|
||||
rc = composition_bounds_[location];
|
||||
else
|
||||
return;
|
||||
|
||||
HIMC imc = ::ImmGetContext(hwnd_);
|
||||
if (imc) {
|
||||
const int kCaretMargin = 1;
|
||||
if (PRIMARYLANGID(input_language_id_) == LANG_CHINESE) {
|
||||
// Chinese IMEs ignore function calls to ::ImmSetCandidateWindow()
|
||||
// when a user disables TSF (Text Service Framework) and CUAS (Cicero
|
||||
// Unaware Application Support).
|
||||
// On the other hand, when a user enables TSF and CUAS, Chinese IMEs
|
||||
// ignore the position of the current system caret and use the
|
||||
// parameters given to ::ImmSetCandidateWindow() with its 'dwStyle'
|
||||
// parameter CFS_CANDIDATEPOS.
|
||||
// Therefore, we do not only call ::ImmSetCandidateWindow() but also
|
||||
// set the positions of the temporary system caret if it exists.
|
||||
CANDIDATEFORM candidate_position = {
|
||||
0, CFS_CANDIDATEPOS, { rc.x, rc.y }, { 0, 0, 0, 0 }
|
||||
};
|
||||
::ImmSetCandidateWindow(imc, &candidate_position);
|
||||
}
|
||||
if (system_caret_) {
|
||||
switch (PRIMARYLANGID(input_language_id_)) {
|
||||
case LANG_JAPANESE:
|
||||
::SetCaretPos(rc.x, rc.y + rc.height);
|
||||
break;
|
||||
default:
|
||||
::SetCaretPos(rc.x, rc.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (PRIMARYLANGID(input_language_id_) == LANG_KOREAN) {
|
||||
// Korean IMEs require the lower-left corner of the caret to move their
|
||||
// candidate windows.
|
||||
rc.y += kCaretMargin;
|
||||
}
|
||||
|
||||
// Japanese IMEs and Korean IMEs also use the rectangle given to
|
||||
// ::ImmSetCandidateWindow() with its 'dwStyle' parameter CFS_EXCLUDE
|
||||
// Therefore, we also set this parameter here.
|
||||
CANDIDATEFORM exclude_rectangle = {
|
||||
0, CFS_EXCLUDE, { rc.x, rc.y },
|
||||
{ rc.x, rc.y, rc.x + rc.width, rc.y + rc.height }
|
||||
};
|
||||
::ImmSetCandidateWindow(imc, &exclude_rectangle);
|
||||
|
||||
::ImmReleaseContext(hwnd_, imc);
|
||||
}
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::CleanupComposition() {
|
||||
// Notify the IMM attached to the given window to complete the ongoing
|
||||
// composition (when given window is de-activated while composing and
|
||||
// re-activated) and reset the composition status.
|
||||
if (is_composing_) {
|
||||
HIMC imc = ::ImmGetContext(hwnd_);
|
||||
if (imc) {
|
||||
::ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
|
||||
::ImmReleaseContext(hwnd_, imc);
|
||||
}
|
||||
ResetComposition();
|
||||
}
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::ResetComposition() {
|
||||
// Reset the composition status.
|
||||
is_composing_ = false;
|
||||
cursor_index_ = -1;
|
||||
}
|
||||
|
||||
|
||||
void OsrImeHandlerWin::GetCompositionInfo(
|
||||
HIMC imc,
|
||||
LPARAM lparam,
|
||||
CefString &composition_text,
|
||||
std::vector<CefCompositionUnderline> &underlines,
|
||||
int& composition_start) {
|
||||
// We only care about GCS_COMPATTR, GCS_COMPCLAUSE and GCS_CURSORPOS, and
|
||||
// convert them into underlines and selection range respectively.
|
||||
underlines.clear();
|
||||
|
||||
int length = static_cast<int>(composition_text.length());
|
||||
|
||||
// Find out the range selected by the user.
|
||||
int target_start = length;
|
||||
int target_end = length;
|
||||
if (lparam & GCS_COMPATTR)
|
||||
GetCompositionSelectionRange(imc, &target_start, &target_end);
|
||||
|
||||
// Retrieve the selection range information. If CS_NOMOVECARET is specified
|
||||
// it means the cursor should not be moved and we therefore place the caret at
|
||||
// the beginning of the composition string. Otherwise we should honour the
|
||||
// GCS_CURSORPOS value if it's available.
|
||||
// TODO(suzhe): Due to a bug in WebKit we currently can't use selection range
|
||||
// with composition string.
|
||||
// See: https://bugs.webkit.org/show_bug.cgi?id=40805
|
||||
if (!(lparam & CS_NOMOVECARET) && (lparam & GCS_CURSORPOS)) {
|
||||
// IMM32 does not support non-zero-width selection in a composition. So
|
||||
// always use the caret position as selection range.
|
||||
int cursor = ::ImmGetCompositionString(imc, GCS_CURSORPOS, NULL, 0);
|
||||
composition_start = cursor;
|
||||
} else {
|
||||
composition_start = 0;
|
||||
}
|
||||
|
||||
// Retrieve the clause segmentations and convert them to underlines.
|
||||
if (lparam & GCS_COMPCLAUSE)
|
||||
GetCompositionUnderlines(imc, target_start, target_end, underlines);
|
||||
|
||||
// Set default underlines in case there is no clause information.
|
||||
if (!underlines.size()) {
|
||||
CefCompositionUnderline underline;
|
||||
underline.color = ColorUNDERLINE;
|
||||
underline.background_color = ColorBKCOLOR;
|
||||
if (target_start > 0) {
|
||||
underline.range.from = 0;
|
||||
underline.range.to = target_start;
|
||||
underline.thick = 0;
|
||||
underlines.push_back(underline);
|
||||
}
|
||||
if (target_end > target_start) {
|
||||
underline.range.from = target_start;
|
||||
underline.range.to = target_end;
|
||||
underline.thick = 1;
|
||||
underlines.push_back(underline);
|
||||
}
|
||||
if (target_end < length) {
|
||||
underline.range.from = target_end;
|
||||
underline.range.to = length;
|
||||
underline.thick = 0;
|
||||
underlines.push_back(underline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OsrImeHandlerWin::GetString(HIMC imc, WPARAM lparam, int type,
|
||||
CefString& result) {
|
||||
if (!(lparam & type))
|
||||
return false;
|
||||
LONG string_size = ::ImmGetCompositionString(imc, type, NULL, 0);
|
||||
if (string_size <= 0)
|
||||
return false;
|
||||
|
||||
// For trailing NULL - ImmGetCompositionString excludes that.
|
||||
string_size += sizeof(WCHAR);
|
||||
|
||||
std::vector<wchar_t> buffer(string_size);
|
||||
::ImmGetCompositionString(imc, type, &buffer[0], string_size);
|
||||
result.FromWString(&buffer[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OsrImeHandlerWin::GetResult(LPARAM lparam, CefString& result) {
|
||||
bool ret = false;
|
||||
HIMC imc = ::ImmGetContext(hwnd_);
|
||||
if (imc) {
|
||||
ret = GetString(imc, lparam, GCS_RESULTSTR, result);
|
||||
::ImmReleaseContext(hwnd_, imc);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool OsrImeHandlerWin::GetComposition(
|
||||
LPARAM lparam,
|
||||
CefString &composition_text,
|
||||
std::vector<CefCompositionUnderline> &underlines,
|
||||
int& composition_start) {
|
||||
bool ret = false;
|
||||
HIMC imc = ::ImmGetContext(hwnd_);
|
||||
if (imc) {
|
||||
// Copy the composition string to the CompositionText object.
|
||||
ret = GetString(imc, lparam, GCS_COMPSTR, composition_text);
|
||||
|
||||
if (ret) {
|
||||
// Retrieve the composition underlines and selection range information.
|
||||
GetCompositionInfo(imc, lparam, composition_text, underlines,
|
||||
composition_start);
|
||||
|
||||
// Mark that there is an ongoing composition.
|
||||
is_composing_ = true;
|
||||
}
|
||||
|
||||
::ImmReleaseContext(hwnd_, imc);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::DisableIME() {
|
||||
CleanupComposition();
|
||||
::ImmAssociateContextEx(hwnd_, NULL, 0);
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::CancelIME() {
|
||||
if (is_composing_) {
|
||||
HIMC imc = ::ImmGetContext(hwnd_);
|
||||
if (imc) {
|
||||
::ImmNotifyIME(imc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
|
||||
::ImmReleaseContext(hwnd_, imc);
|
||||
}
|
||||
ResetComposition();
|
||||
}
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::EnableIME() {
|
||||
// Load the default IME context.
|
||||
::ImmAssociateContextEx(hwnd_, NULL, IACE_DEFAULT);
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::UpdateCaretPosition(int index) {
|
||||
// Save the caret position.
|
||||
cursor_index_ = index;
|
||||
// Move the IME window.
|
||||
MoveImeWindow();
|
||||
}
|
||||
|
||||
void OsrImeHandlerWin::ChangeCompositionRange(
|
||||
const CefRange& selection_range,
|
||||
const std::vector<CefRect>& bounds) {
|
||||
composition_range_ = selection_range;
|
||||
composition_bounds_ = bounds;
|
||||
MoveImeWindow();
|
||||
}
|
||||
|
||||
} // namespace client
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
|
||||
#include "include/internal/cef_types_wrappers.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
// Handles IME for the native parent window that hosts an off-screen browser.
|
||||
// This object is only accessed on the CEF UI thread.
|
||||
class OsrImeHandlerWin {
|
||||
public:
|
||||
explicit OsrImeHandlerWin(HWND hwnd);
|
||||
virtual ~OsrImeHandlerWin();
|
||||
|
||||
// Retrieves whether or not there is an ongoing composition.
|
||||
bool is_composing() const { return is_composing_; }
|
||||
|
||||
// Retrieves the input language from Windows and update it.
|
||||
void SetInputLanguage();
|
||||
|
||||
// Creates the IME caret windows if required.
|
||||
void CreateImeWindow();
|
||||
|
||||
// Destroys the IME caret windows.
|
||||
void DestroyImeWindow();
|
||||
|
||||
// Cleans up the all resources attached to the given IMM32Manager object, and
|
||||
// reset its composition status.
|
||||
void CleanupComposition();
|
||||
|
||||
// Resets the composition status and cancels the ongoing composition.
|
||||
void ResetComposition();
|
||||
|
||||
// Retrieves a composition result of the ongoing composition if it exists.
|
||||
bool GetResult(LPARAM lparam, CefString& result);
|
||||
|
||||
// Retrieves the current composition status of the ongoing composition.
|
||||
// Includes composition text, underline information and selection range in the
|
||||
// composition text. IMM32 does not support char selection.
|
||||
bool GetComposition(LPARAM lparam, CefString &composition_text,
|
||||
std::vector<CefCompositionUnderline> &underlines,
|
||||
int& composition_start);
|
||||
|
||||
// Enables the IME attached to the given window.
|
||||
virtual void EnableIME();
|
||||
|
||||
// Disables the IME attached to the given window.
|
||||
virtual void DisableIME();
|
||||
|
||||
// Cancels an ongoing composition of the IME.
|
||||
virtual void CancelIME();
|
||||
|
||||
// Updates the IME caret position of the given window.
|
||||
void UpdateCaretPosition(int index);
|
||||
|
||||
// Updates the composition range. |selected_range| is the range of characters
|
||||
// that have been selected. |character_bounds| is the bounds of each character
|
||||
// in view device coordinates.
|
||||
void ChangeCompositionRange(const CefRange& selection_range,
|
||||
const std::vector<CefRect>& character_bounds);
|
||||
|
||||
// Updates the position of the IME windows.
|
||||
void MoveImeWindow();
|
||||
|
||||
private:
|
||||
// Retrieves the composition information.
|
||||
void GetCompositionInfo(HIMC imm_context, LPARAM lparam,
|
||||
CefString &composition_text,
|
||||
std::vector<CefCompositionUnderline>& underlines,
|
||||
int& composition_start);
|
||||
|
||||
// Retrieves a string from the IMM.
|
||||
bool GetString(HIMC imm_context, WPARAM lparam, int type, CefString& result);
|
||||
|
||||
// Represents whether or not there is an ongoing composition.
|
||||
bool is_composing_;
|
||||
|
||||
// The current composition character range and its bounds.
|
||||
std::vector<CefRect> composition_bounds_;
|
||||
|
||||
// This value represents whether or not the current input context has IMEs.
|
||||
bool ime_status_;
|
||||
|
||||
// The current input Language ID retrieved from Windows -
|
||||
// used for processing language-specific operations in IME.
|
||||
LANGID input_language_id_;
|
||||
|
||||
// Represents whether or not the current input context has created a system
|
||||
// caret to set the position of its IME candidate window.
|
||||
bool system_caret_;
|
||||
|
||||
// The rectangle of the input caret retrieved from a renderer process.
|
||||
CefRect ime_rect_;
|
||||
|
||||
// The current cursor index in composition string.
|
||||
int cursor_index_;
|
||||
|
||||
// The composition range in the string. This may be used to determine the
|
||||
// offset in composition bounds.
|
||||
CefRange composition_range_;
|
||||
|
||||
// Hwnd associated with this instance.
|
||||
HWND hwnd_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_IME_HANDLER_WIN_H_
|
|
@ -11,6 +11,7 @@
|
|||
#include "cefclient/browser/main_message_loop.h"
|
||||
#include "cefclient/browser/resource.h"
|
||||
#include "cefclient/browser/util_win.h"
|
||||
#include "cefclient/browser/osr_ime_handler_win.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
|
@ -55,7 +56,6 @@ OsrWindowWin::OsrWindowWin(Delegate* delegate,
|
|||
hwnd_(NULL),
|
||||
hdc_(NULL),
|
||||
hrc_(NULL),
|
||||
client_rect_(),
|
||||
device_scale_factor_(client::GetDeviceScaleFactor()),
|
||||
painting_popup_(false),
|
||||
render_task_pending_(false),
|
||||
|
@ -255,6 +255,8 @@ void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {
|
|||
DCHECK_EQ(register_res, S_OK);
|
||||
#endif
|
||||
|
||||
ime_handler_.reset(new OsrImeHandlerWin(hwnd_));
|
||||
|
||||
// Notify the window owner.
|
||||
NotifyNativeWindowCreated(hwnd_);
|
||||
}
|
||||
|
@ -273,6 +275,7 @@ void OsrWindowWin::Destroy() {
|
|||
|
||||
// Destroy the native window.
|
||||
::DestroyWindow(hwnd_);
|
||||
ime_handler_.reset();
|
||||
hwnd_ = NULL;
|
||||
}
|
||||
|
||||
|
@ -390,6 +393,74 @@ void OsrWindowWin::RegisterOsrClass(HINSTANCE hInstance,
|
|||
RegisterClassEx(&wcex);
|
||||
}
|
||||
|
||||
void OsrWindowWin::OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam) {
|
||||
// We handle the IME Composition Window ourselves (but let the IME Candidates
|
||||
// Window be handled by IME through DefWindowProc()), so clear the
|
||||
// ISC_SHOWUICOMPOSITIONWINDOW flag:
|
||||
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
||||
::DefWindowProc(hwnd_, message, wParam, lParam);
|
||||
|
||||
// Create Caret Window if required
|
||||
if (ime_handler_) {
|
||||
ime_handler_->CreateImeWindow();
|
||||
ime_handler_->MoveImeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void OsrWindowWin::OnIMEStartComposition() {
|
||||
if (ime_handler_) {
|
||||
ime_handler_->CreateImeWindow();
|
||||
ime_handler_->MoveImeWindow();
|
||||
ime_handler_->ResetComposition();
|
||||
}
|
||||
}
|
||||
|
||||
void OsrWindowWin::OnIMEComposition(UINT message, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
if (browser_ && ime_handler_) {
|
||||
CefString cTextStr;
|
||||
if (ime_handler_->GetResult(lParam, cTextStr)) {
|
||||
// Send the text to the browser. The |replacement_range| and
|
||||
// |relative_cursor_pos| params are not used on Windows, so provide
|
||||
// default invalid values.
|
||||
browser_->GetHost()->ImeCommitText(cTextStr,
|
||||
CefRange(UINT32_MAX, UINT32_MAX), 0);
|
||||
ime_handler_->ResetComposition();
|
||||
// Continue reading the composition string - Japanese IMEs send both
|
||||
// GCS_RESULTSTR and GCS_COMPSTR.
|
||||
}
|
||||
|
||||
std::vector<CefCompositionUnderline> underlines;
|
||||
int composition_start = 0;
|
||||
|
||||
if (ime_handler_->GetComposition(lParam, cTextStr, underlines,
|
||||
composition_start)) {
|
||||
// Send the composition string to the browser. The |replacement_range|
|
||||
// param is not used on Windows, so provide a default invalid value.
|
||||
browser_->GetHost()->ImeSetComposition(cTextStr, underlines,
|
||||
CefRange(UINT32_MAX, UINT32_MAX),
|
||||
CefRange(composition_start,
|
||||
static_cast<int>(composition_start + cTextStr.length())));
|
||||
|
||||
// Update the Candidate Window position. The cursor is at the end so
|
||||
// subtract 1. This is safe because IMM32 does not support non-zero-width
|
||||
// in a composition. Also, negative values are safely ignored in
|
||||
// MoveImeWindow
|
||||
ime_handler_->UpdateCaretPosition(composition_start - 1);
|
||||
} else {
|
||||
OnIMECancelCompositionEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsrWindowWin::OnIMECancelCompositionEvent() {
|
||||
if (browser_ && ime_handler_) {
|
||||
browser_->GetHost()->ImeCancelComposition();
|
||||
ime_handler_->ResetComposition();
|
||||
ime_handler_->DestroyImeWindow();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, UINT message,
|
||||
WPARAM wParam, LPARAM lParam) {
|
||||
|
@ -399,7 +470,22 @@ LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, UINT message,
|
|||
if (!self)
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
|
||||
// We want to handle IME events before the OS does any default handling.
|
||||
switch (message) {
|
||||
case WM_IME_SETCONTEXT:
|
||||
self->OnIMESetContext(message, wParam, lParam);
|
||||
return 0;
|
||||
case WM_IME_STARTCOMPOSITION:
|
||||
self->OnIMEStartComposition();
|
||||
return 0;
|
||||
case WM_IME_COMPOSITION:
|
||||
self->OnIMEComposition(message, wParam, lParam);
|
||||
return 0;
|
||||
case WM_IME_ENDCOMPOSITION:
|
||||
self->OnIMECancelCompositionEvent();
|
||||
// Let WTL call::DefWindowProc() and release its resources.
|
||||
break;
|
||||
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
|
@ -909,6 +995,24 @@ void OsrWindowWin::UpdateDragCursor(
|
|||
#endif
|
||||
}
|
||||
|
||||
void OsrWindowWin::OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
if (ime_handler_) {
|
||||
// Convert from view coordinates to device coordinates.
|
||||
CefRenderHandler::RectList device_bounds;
|
||||
CefRenderHandler::RectList::const_iterator it = character_bounds.begin();
|
||||
for (; it != character_bounds.end(); ++it) {
|
||||
device_bounds.push_back(LogicalToDevice(*it, device_scale_factor_));
|
||||
}
|
||||
|
||||
ime_handler_->ChangeCompositionRange(selection_range, device_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
|
||||
CefBrowserHost::DragOperationsMask
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
namespace client {
|
||||
|
||||
class OsrImeHandlerWin;
|
||||
|
||||
// Represents the native parent window for an off-screen browser. This object
|
||||
// must live on the CEF UI thread in order to handle CefRenderHandler callbacks.
|
||||
// The methods of this class are thread-safe unless otherwise indicated.
|
||||
|
@ -94,6 +96,11 @@ class OsrWindowWin :
|
|||
void OnPaint();
|
||||
bool OnEraseBkgnd();
|
||||
|
||||
void OnIMESetContext(UINT message, WPARAM wParam, LPARAM lParam);
|
||||
void OnIMEStartComposition();
|
||||
void OnIMEComposition(UINT message, WPARAM wParam, LPARAM lParam);
|
||||
void OnIMECancelCompositionEvent();
|
||||
|
||||
// Manage popup bounds.
|
||||
bool IsOverPopupWidget(int x, int y) const;
|
||||
int GetPopupXOffset() const;
|
||||
|
@ -133,6 +140,10 @@ class OsrWindowWin :
|
|||
int x, int y) OVERRIDE;
|
||||
void UpdateDragCursor(CefRefPtr<CefBrowser> browser,
|
||||
CefRenderHandler::DragOperation operation) OVERRIDE;
|
||||
void OnImeCompositionRangeChanged(
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
// OsrDragEvents methods.
|
||||
|
@ -156,6 +167,9 @@ class OsrWindowWin :
|
|||
HDC hdc_;
|
||||
HGLRC hrc_;
|
||||
|
||||
// Class that encapsulates IMM32 APIs and controls IMEs attached to a window.
|
||||
scoped_ptr<OsrImeHandlerWin> ime_handler_;
|
||||
|
||||
RECT client_rect_;
|
||||
float device_scale_factor_;
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
|
||||
#pragma once
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "include/cef_browser.h"
|
||||
#include "include/cef_render_handler.h"
|
||||
|
||||
// Implementation for the NSTextInputClient protocol used for enabling IME on
|
||||
// mac when window rendering is disabled.
|
||||
|
||||
@interface CefTextInputClientOSRMac : NSObject<NSTextInputClient> {
|
||||
@private
|
||||
|
||||
// The range of current marked text inside the whole content of the DOM node
|
||||
// being edited.
|
||||
NSRange markedRange_;
|
||||
|
||||
// The current composition character range and its bounds.
|
||||
CefRange composition_range_;
|
||||
std::vector<CefRect> composition_bounds_;
|
||||
|
||||
// Represents the input-method attributes supported by this object.
|
||||
NSArray* validAttributesForMarkedText_;
|
||||
|
||||
// Indicates if we are currently handling a key down event.
|
||||
BOOL handlingKeyDown_;
|
||||
|
||||
// Indicates if there is any marked text.
|
||||
BOOL hasMarkedText_;
|
||||
|
||||
// Indicates whether there was any marked text prior to handling
|
||||
// the current key event.
|
||||
BOOL oldHasMarkedText_;
|
||||
|
||||
// Indicates if unmarkText is called or not when handling a keyboard
|
||||
// event.
|
||||
BOOL unmarkTextCalled_;
|
||||
|
||||
// The selected range, cached from a message sent by the renderer.
|
||||
NSRange selectedRange_;
|
||||
|
||||
// Text to be inserted which was generated by handling a key down event.
|
||||
std::string textToBeInserted_;
|
||||
|
||||
// Marked text which was generated by handling a key down event.
|
||||
CefString markedText_;
|
||||
|
||||
// Underline information of the |markedText_|.
|
||||
std::vector<CefCompositionUnderline> underlines_;
|
||||
|
||||
// Replacement range information received from |setMarkedText:|.
|
||||
CefRange setMarkedTextReplacementRange_;
|
||||
|
||||
CefRefPtr<CefBrowser> browser_;
|
||||
}
|
||||
|
||||
@property(nonatomic, readonly) NSRange selectedRange;
|
||||
@property(nonatomic) BOOL handlingKeyDown;
|
||||
|
||||
- (id)initWithBrowser:(CefRefPtr<CefBrowser>)browser;
|
||||
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent;
|
||||
- (void)HandleKeyEventAfterTextInputClient:(CefKeyEvent)keyEvent;
|
||||
- (void)ChangeCompositionRange:(CefRange)range
|
||||
character_bounds:(const CefRenderHandler::RectList&)bounds;
|
||||
- (void)cancelComposition;
|
||||
|
||||
@end
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_TEXT_INPUT_CLIENT_OSR_MAC_H_
|
|
@ -0,0 +1,343 @@
|
|||
// Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// Implementation based on
|
||||
// content/browser/renderer_host/render_widget_host_view_mac.mm from Chromium.
|
||||
|
||||
#include "text_input_client_osr_mac.h"
|
||||
#include "include/cef_client.h"
|
||||
|
||||
#define ColorBLACK 0xFF000000 // Same as Blink SKColor.
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(suzhe): Upstream this function.
|
||||
cef_color_t CefColorFromNSColor(NSColor *color) {
|
||||
CGFloat r, g, b, a;
|
||||
[color getRed:&r green:&g blue:&b alpha:&a];
|
||||
|
||||
return
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * a)), 255)) << 24 |
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * r)), 255)) << 16 |
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * g)), 255)) << 8 |
|
||||
std::max(0, std::min(static_cast<int>(lroundf(255.0f * b)), 255));
|
||||
}
|
||||
|
||||
// Extract underline information from an attributed string. Mostly copied from
|
||||
// third_party/WebKit/Source/WebKit/mac/WebView/WebHTMLView.mm
|
||||
void ExtractUnderlines(NSAttributedString* string,
|
||||
std::vector<CefCompositionUnderline>* underlines) {
|
||||
int length = [[string string] length];
|
||||
int i = 0;
|
||||
while (i < length) {
|
||||
NSRange range;
|
||||
NSDictionary* attrs = [string attributesAtIndex:i
|
||||
longestEffectiveRange:&range
|
||||
inRange:NSMakeRange(i, length - i)];
|
||||
NSNumber *style = [attrs objectForKey: NSUnderlineStyleAttributeName];
|
||||
if (style) {
|
||||
cef_color_t color = ColorBLACK;
|
||||
if (NSColor *colorAttr =
|
||||
[attrs objectForKey:NSUnderlineColorAttributeName]) {
|
||||
color = CefColorFromNSColor(
|
||||
[colorAttr colorUsingColorSpaceName:NSDeviceRGBColorSpace]);
|
||||
}
|
||||
cef_composition_underline_t line = {
|
||||
{range.location, NSMaxRange(range)}, color, 0, [style intValue] > 1
|
||||
};
|
||||
underlines->push_back(line);
|
||||
}
|
||||
i = range.location + range.length;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
extern NSString* NSTextInputReplacementRangeAttributeName;
|
||||
}
|
||||
|
||||
@implementation CefTextInputClientOSRMac
|
||||
|
||||
@synthesize selectedRange = selectedRange_;
|
||||
@synthesize handlingKeyDown = handlingKeyDown_;
|
||||
|
||||
- (id)initWithBrowser:(CefRefPtr<CefBrowser>)browser {
|
||||
self = [super init];
|
||||
browser_ = browser;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSArray*)validAttributesForMarkedText {
|
||||
if (!validAttributesForMarkedText_) {
|
||||
validAttributesForMarkedText_ = [[NSArray alloc] initWithObjects:
|
||||
NSUnderlineStyleAttributeName,
|
||||
NSUnderlineColorAttributeName,
|
||||
NSMarkedClauseSegmentAttributeName,
|
||||
NSTextInputReplacementRangeAttributeName,
|
||||
nil];
|
||||
}
|
||||
return validAttributesForMarkedText_;
|
||||
}
|
||||
|
||||
- (NSRange)markedRange {
|
||||
return hasMarkedText_ ? markedRange_ : NSMakeRange(NSNotFound, 0);
|
||||
}
|
||||
|
||||
- (BOOL)hasMarkedText {
|
||||
return hasMarkedText_;
|
||||
}
|
||||
|
||||
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
|
||||
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
|
||||
NSString* im_text = isAttributedString ? [aString string] : aString;
|
||||
if (handlingKeyDown_) {
|
||||
textToBeInserted_.append([im_text UTF8String]);
|
||||
} else {
|
||||
cef_range_t range = {
|
||||
replacementRange.location,
|
||||
NSMaxRange(replacementRange)
|
||||
};
|
||||
browser_->GetHost()->ImeCommitText([im_text UTF8String], range, 0);
|
||||
}
|
||||
|
||||
// Inserting text will delete all marked text automatically.
|
||||
hasMarkedText_ = NO;
|
||||
}
|
||||
|
||||
- (void)doCommandBySelector:(SEL)aSelector {
|
||||
// An input method calls this function to dispatch an editing command to be
|
||||
// handled by this view.
|
||||
}
|
||||
|
||||
- (void)setMarkedText:(id)aString
|
||||
selectedRange:(NSRange)newSelRange
|
||||
replacementRange:(NSRange)replacementRange {
|
||||
// An input method has updated the composition string. We send the given text
|
||||
// and range to the browser so it can update the composition node of Blink.
|
||||
|
||||
BOOL isAttributedString = [aString isKindOfClass:[NSAttributedString class]];
|
||||
NSString* im_text = isAttributedString ? [aString string] : aString;
|
||||
int length = [im_text length];
|
||||
|
||||
// |markedRange_| will get set in a callback from ImeSetComposition().
|
||||
selectedRange_ = newSelRange;
|
||||
markedText_ = [im_text UTF8String];
|
||||
hasMarkedText_ = (length > 0);
|
||||
underlines_.clear();
|
||||
|
||||
if (isAttributedString) {
|
||||
ExtractUnderlines(aString, &underlines_);
|
||||
} else {
|
||||
// Use a thin black underline by default.
|
||||
cef_composition_underline_t line = {
|
||||
{0, length}, ColorBLACK, 0, false
|
||||
};
|
||||
underlines_.push_back(line);
|
||||
}
|
||||
|
||||
// If we are handling a key down event then ImeSetComposition() will be
|
||||
// called from the keyEvent: method.
|
||||
// Input methods of Mac use setMarkedText calls with empty text to cancel an
|
||||
// ongoing composition. Our input method backend will automatically cancel an
|
||||
// ongoing composition when we send empty text.
|
||||
if (handlingKeyDown_) {
|
||||
setMarkedTextReplacementRange_ = {
|
||||
replacementRange.location,
|
||||
NSMaxRange(replacementRange)
|
||||
};
|
||||
} else if (!handlingKeyDown_) {
|
||||
CefRange replacement_range(replacementRange.location,
|
||||
NSMaxRange(replacementRange));
|
||||
CefRange selection_range(newSelRange.location, NSMaxRange(newSelRange));
|
||||
|
||||
browser_->GetHost()->ImeSetComposition(
|
||||
markedText_, underlines_, replacement_range, selection_range);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)unmarkText {
|
||||
// Delete the composition node of the browser and finish an ongoing
|
||||
// composition.
|
||||
// It seems that, instead of calling this method, an input method will call
|
||||
// the setMarkedText method with empty text to cancel ongoing composition.
|
||||
// Implement this method even though we don't expect it to be called.
|
||||
hasMarkedText_ = NO;
|
||||
markedText_.clear();
|
||||
underlines_.clear();
|
||||
|
||||
// If we are handling a key down event then ImeFinishComposingText() will be
|
||||
// called from the keyEvent: method.
|
||||
if (!handlingKeyDown_)
|
||||
browser_->GetHost()->ImeFinishComposingText(false);
|
||||
else
|
||||
unmarkTextCalled_ = YES;
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
|
||||
actualRange:(NSRangePointer)actualRange {
|
||||
// Modify the attributed string if required.
|
||||
// Not implemented here as we do not want to control the IME window view.
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSRect)firstViewRectForCharacterRange:(NSRange)theRange
|
||||
actualRange:(NSRangePointer)actualRange {
|
||||
NSRect rect;
|
||||
|
||||
NSUInteger location = theRange.location;
|
||||
|
||||
// If location is not specified fall back to the composition range start.
|
||||
if (location == NSNotFound)
|
||||
location = markedRange_.location;
|
||||
|
||||
// Offset location by the composition range start if required.
|
||||
if (location >= markedRange_.location)
|
||||
location -= markedRange_.location;
|
||||
|
||||
if(location < composition_bounds_.size()) {
|
||||
const CefRect& rc = composition_bounds_[location];
|
||||
rect = NSMakeRect(rc.x, rc.y, rc.width, rc.height);
|
||||
}
|
||||
|
||||
if (actualRange)
|
||||
*actualRange = NSMakeRange(location, theRange.length);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (NSRect)screenRectFromViewRect:(NSRect)rect {
|
||||
NSRect screenRect;
|
||||
|
||||
int screenX, screenY;
|
||||
browser_->GetHost()->GetClient()->GetRenderHandler()->GetScreenPoint(
|
||||
browser_, rect.origin.x, rect.origin.y, screenX, screenY);
|
||||
screenRect.origin = NSMakePoint(screenX, screenY);
|
||||
screenRect.size = rect.size;
|
||||
|
||||
return screenRect;
|
||||
}
|
||||
|
||||
- (NSRect)firstRectForCharacterRange:(NSRange)theRange
|
||||
actualRange:(NSRangePointer)actualRange {
|
||||
NSRect rect = [self firstViewRectForCharacterRange:theRange
|
||||
actualRange:actualRange];
|
||||
|
||||
// Convert into screen coordinates for return.
|
||||
rect = [self screenRectFromViewRect:rect];
|
||||
|
||||
if (rect.origin.y >= rect.size.height)
|
||||
rect.origin.y -= rect.size.height;
|
||||
else
|
||||
rect.origin.y = 0;
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (void)HandleKeyEventBeforeTextInputClient:(NSEvent*)keyEvent {
|
||||
DCHECK([keyEvent type] == NSKeyDown);
|
||||
// Don't call this method recursively.
|
||||
DCHECK(!handlingKeyDown_);
|
||||
|
||||
oldHasMarkedText_ = hasMarkedText_;
|
||||
handlingKeyDown_ = YES;
|
||||
|
||||
// These variables might be set when handling the keyboard event.
|
||||
// Clear them here so that we can know whether they have changed afterwards.
|
||||
textToBeInserted_.clear();
|
||||
markedText_.clear();
|
||||
underlines_.clear();
|
||||
setMarkedTextReplacementRange_ = CefRange(UINT32_MAX, UINT32_MAX);
|
||||
unmarkTextCalled_ = NO;
|
||||
}
|
||||
|
||||
- (void)HandleKeyEventAfterTextInputClient:(CefKeyEvent)keyEvent {
|
||||
handlingKeyDown_ = NO;
|
||||
|
||||
// Send keypress and/or composition related events.
|
||||
// Note that |textToBeInserted_| is a UTF-16 string but it's fine to only
|
||||
// handle BMP characters here as we can always insert non-BMP characters as
|
||||
// text.
|
||||
|
||||
// If the text to be inserted only contains 1 character then we can just send
|
||||
// a keypress event.
|
||||
if (!hasMarkedText_ && !oldHasMarkedText_ &&
|
||||
textToBeInserted_.length() <= 1) {
|
||||
keyEvent.type = KEYEVENT_KEYDOWN;
|
||||
|
||||
browser_->GetHost()->SendKeyEvent(keyEvent);
|
||||
|
||||
// Don't send a CHAR event for non-char keys like arrows, function keys and
|
||||
// clear.
|
||||
if (keyEvent.modifiers & (EVENTFLAG_IS_KEY_PAD)) {
|
||||
if(keyEvent.native_key_code == 71)
|
||||
return;
|
||||
}
|
||||
|
||||
keyEvent.type = KEYEVENT_CHAR;
|
||||
browser_->GetHost()->SendKeyEvent(keyEvent);
|
||||
}
|
||||
|
||||
// If the text to be inserted contains multiple characters then send the text
|
||||
// to the browser using ImeCommitText().
|
||||
BOOL textInserted = NO;
|
||||
if (textToBeInserted_.length() >
|
||||
((hasMarkedText_ || oldHasMarkedText_) ? 0u : 1u)) {
|
||||
browser_->GetHost()->ImeCommitText(
|
||||
textToBeInserted_, CefRange(UINT32_MAX, UINT32_MAX), 0);
|
||||
textToBeInserted_.clear();
|
||||
}
|
||||
|
||||
// Update or cancel the composition. If some text has been inserted then we
|
||||
// don't need to explicitly cancel the composition.
|
||||
if (hasMarkedText_ && markedText_.length()) {
|
||||
// Update the composition by sending marked text to the browser.
|
||||
// |selectedRange_| is the range being selected inside the marked text.
|
||||
browser_->GetHost()->ImeSetComposition(
|
||||
markedText_, underlines_, setMarkedTextReplacementRange_,
|
||||
CefRange(selectedRange_.location, NSMaxRange(selectedRange_)));
|
||||
} else if (oldHasMarkedText_ && !hasMarkedText_ && !textInserted) {
|
||||
// There was no marked text or inserted text. Complete or cancel the
|
||||
// composition.
|
||||
if (unmarkTextCalled_)
|
||||
browser_->GetHost()->ImeFinishComposingText(false);
|
||||
else
|
||||
browser_->GetHost()->ImeCancelComposition();
|
||||
}
|
||||
|
||||
setMarkedTextReplacementRange_ = CefRange(UINT32_MAX, UINT32_MAX);
|
||||
}
|
||||
|
||||
- (void)ChangeCompositionRange:(CefRange)range
|
||||
character_bounds:(const CefRenderHandler::RectList&) bounds {
|
||||
composition_range_ = range;
|
||||
markedRange_ = NSMakeRange(range.from, range.to - range.from);
|
||||
composition_bounds_ = bounds;
|
||||
}
|
||||
|
||||
- (void)cancelComposition {
|
||||
if (!hasMarkedText_)
|
||||
return;
|
||||
|
||||
// Cancel the ongoing composition. [NSInputManager markedTextAbandoned:]
|
||||
// doesn't call any NSTextInput functions, such as setMarkedText or
|
||||
// insertText.
|
||||
// TODO(erikchen): NSInputManager is deprecated since OSX 10.6. Switch to
|
||||
// NSTextInputContext. http://www.crbug.com/479010.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSInputManager* currentInputManager = [NSInputManager currentInputManager];
|
||||
[currentInputManager markedTextAbandoned:self];
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
hasMarkedText_ = NO;
|
||||
// Should not call [self unmarkText] here because it'll send unnecessary
|
||||
// cancel composition messages to the browser.
|
||||
}
|
||||
|
||||
@end
|
|
@ -173,6 +173,14 @@ enum OSRTestType {
|
|||
OSR_TEST_DRAG_DROP_UPDATE_CURSOR,
|
||||
// dropping element inside drop region will move the element
|
||||
OSR_TEST_DRAG_DROP_DROP,
|
||||
// IMESetComposition will update the composition range
|
||||
OSR_TEST_IME_SET_COMPOSITION,
|
||||
// IMECommitText inserts the specified text
|
||||
OSR_TEST_IME_COMMIT_TEXT,
|
||||
// IMEFinishComposition will commit the text present composition text
|
||||
OSR_TEST_IME_FINISH_COMPOSITION,
|
||||
// IMECancelComposition will update the composition range
|
||||
OSR_TEST_IME_CANCEL_COMPOSITION,
|
||||
|
||||
// Define the range for popup tests.
|
||||
OSR_TEST_POPUP_FIRST = OSR_TEST_POPUP_PAINT,
|
||||
|
@ -224,6 +232,27 @@ class OSRTestHandler : public RoutingTestHandler,
|
|||
frame->GetURL().ToString().c_str());
|
||||
DestroySucceededTestSoon();
|
||||
} break;
|
||||
case OSR_TEST_IME_COMMIT_TEXT: {
|
||||
const std::string& expected_url =
|
||||
std::string(kTestUrl) + "?k=osrimecommit";
|
||||
EXPECT_STREQ(expected_url.c_str(),
|
||||
frame->GetURL().ToString().c_str());
|
||||
DestroySucceededTestSoon();
|
||||
} break;
|
||||
case OSR_TEST_IME_FINISH_COMPOSITION: {
|
||||
const std::string& expected_url =
|
||||
std::string(kTestUrl) + "?k=" + kKeyTestWord;
|
||||
EXPECT_STREQ(expected_url.c_str(),
|
||||
frame->GetURL().ToString().c_str());
|
||||
DestroySucceededTestSoon();
|
||||
} break;
|
||||
case OSR_TEST_IME_CANCEL_COMPOSITION: {
|
||||
const std::string& expected_url =
|
||||
std::string(kTestUrl) + "?k=";
|
||||
EXPECT_STREQ(expected_url.c_str(),
|
||||
frame->GetURL().ToString().c_str());
|
||||
DestroySucceededTestSoon();
|
||||
} break;
|
||||
default:
|
||||
// Intentionally left blank
|
||||
break;
|
||||
|
@ -717,6 +746,169 @@ class OSRTestHandler : public RoutingTestHandler,
|
|||
}
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_IME_COMMIT_TEXT:
|
||||
{
|
||||
// trigger the IME Set Composition event
|
||||
if (StartTest()) {
|
||||
// click inside edit box so that text could be entered
|
||||
CefMouseEvent mouse_event;
|
||||
mouse_event.x = MiddleX(kEditBoxRect);
|
||||
mouse_event.y = MiddleY(kEditBoxRect);
|
||||
mouse_event.modifiers = 0;
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, true, 1);
|
||||
|
||||
size_t word_length = strlen(kKeyTestWord);
|
||||
// Add some input keys to edit box
|
||||
for (size_t i = 0; i < word_length; ++i) {
|
||||
#if defined(OS_WIN)
|
||||
SendKeyEvent(browser, kKeyTestWord[i]);
|
||||
#elif defined(OS_MACOSX)
|
||||
SendKeyEvent(browser, kKeyTestCodes[i]);
|
||||
#elif defined(OS_LINUX)
|
||||
SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
// This text should be honored instead of 'ka' added via key events
|
||||
CefString markedText("osrimecommit");
|
||||
|
||||
CefRange range(0, markedText.length());
|
||||
browser->GetHost()->ImeCommitText(markedText, range, 0);
|
||||
|
||||
// click button to navigate
|
||||
mouse_event.x = MiddleX(kNavigateButtonRect);
|
||||
mouse_event.y = MiddleY(kNavigateButtonRect);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, true, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_IME_FINISH_COMPOSITION:
|
||||
{
|
||||
// trigger the IME Set Composition event
|
||||
if (StartTest()) {
|
||||
// click inside edit box so that text could be entered
|
||||
CefMouseEvent mouse_event;
|
||||
mouse_event.x = MiddleX(kEditBoxRect);
|
||||
mouse_event.y = MiddleY(kEditBoxRect);
|
||||
mouse_event.modifiers = 0;
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
|
||||
false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
|
||||
true, 1);
|
||||
|
||||
size_t word_length = strlen(kKeyTestWord);
|
||||
// Add some input keys to edit box
|
||||
for (size_t i = 0; i < word_length; ++i) {
|
||||
#if defined(OS_WIN)
|
||||
SendKeyEvent(browser, kKeyTestWord[i]);
|
||||
#elif defined(OS_MACOSX)
|
||||
SendKeyEvent(browser, kKeyTestCodes[i]);
|
||||
#elif defined(OS_LINUX)
|
||||
SendKeyEvent(browser, kNativeKeyTestCodes[i], kKeyTestCodes[i]);
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
// Finish Composition should set the existing composition
|
||||
browser->GetHost()->ImeFinishComposingText(true);
|
||||
|
||||
// click button to navigate
|
||||
mouse_event.x = MiddleX(kNavigateButtonRect);
|
||||
mouse_event.y = MiddleY(kNavigateButtonRect);
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
|
||||
false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT,
|
||||
true, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_IME_CANCEL_COMPOSITION:
|
||||
{
|
||||
// trigger the IME Set Composition event
|
||||
if (StartTest()) {
|
||||
// click inside edit box so that text could be entered
|
||||
CefMouseEvent mouse_event;
|
||||
mouse_event.x = MiddleX(kEditBoxRect);
|
||||
mouse_event.y = MiddleY(kEditBoxRect);
|
||||
mouse_event.modifiers = 0;
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, true, 1);
|
||||
// Add some input keys to edit box
|
||||
CefString markedText("か");
|
||||
std::vector<CefCompositionUnderline> underlines;
|
||||
|
||||
// Use a thin black underline by default.
|
||||
cef_range_t range = {0, markedText.length()};
|
||||
cef_composition_underline_t line = {
|
||||
range, 0xFF000000, 0, false
|
||||
};
|
||||
underlines.push_back(line);
|
||||
|
||||
CefRange replacement_range(0, markedText.length());
|
||||
CefRange selection_range(0,markedText.length());
|
||||
|
||||
// Composition should be updated
|
||||
browser->GetHost()->ImeSetComposition(markedText, underlines,
|
||||
replacement_range,selection_range);
|
||||
|
||||
// CancelComposition should clean up the edit text
|
||||
browser->GetHost()->ImeCancelComposition();
|
||||
|
||||
// click button to navigate and verify
|
||||
mouse_event.x = MiddleX(kNavigateButtonRect);
|
||||
mouse_event.y = MiddleY(kNavigateButtonRect);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, true, 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_IME_SET_COMPOSITION:
|
||||
{
|
||||
// trigger the IME Set Composition event
|
||||
if (StartTest()) {
|
||||
// click inside edit box so that text could be entered
|
||||
CefMouseEvent mouse_event;
|
||||
mouse_event.x = MiddleX(kEditBoxRect);
|
||||
mouse_event.y = MiddleY(kEditBoxRect);
|
||||
mouse_event.modifiers = 0;
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(
|
||||
mouse_event, MBT_LEFT, true, 1);
|
||||
|
||||
// Now set some intermediate text composition
|
||||
CefString markedText("か");
|
||||
std::vector<CefCompositionUnderline> underlines;
|
||||
|
||||
// Use a thin black underline by default.
|
||||
cef_range_t range = {0, markedText.length()};
|
||||
cef_composition_underline_t line = {
|
||||
range, 0xFF000000, 0, false
|
||||
};
|
||||
underlines.push_back(line);
|
||||
|
||||
CefRange replacement_range(0, markedText.length());
|
||||
CefRange selection_range(0,markedText.length());
|
||||
|
||||
// This should update composition range and
|
||||
// trigger the compositionRangeChanged callback
|
||||
browser->GetHost()->ImeSetComposition(markedText, underlines,
|
||||
replacement_range,selection_range);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -750,6 +942,17 @@ class OSRTestHandler : public RoutingTestHandler,
|
|||
}
|
||||
}
|
||||
|
||||
void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& range,
|
||||
const CefRenderHandler::RectList& bounds) override {
|
||||
if (test_type_ == OSR_TEST_IME_SET_COMPOSITION && started()) {
|
||||
EXPECT_EQ(range.from, 0);
|
||||
EXPECT_EQ(range.to, 1);
|
||||
EXPECT_EQ(1U, bounds.size());
|
||||
DestroySucceededTestSoon();
|
||||
}
|
||||
}
|
||||
|
||||
bool StartDragging(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefDragData> drag_data,
|
||||
CefRenderHandler::DragOperationsMask allowed_ops,
|
||||
|
@ -1078,3 +1281,11 @@ OSR_TEST(DragDropUpdateCursor, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 1.0f);
|
|||
OSR_TEST(DragDropUpdateCursor2x, OSR_TEST_DRAG_DROP_UPDATE_CURSOR, 2.0f);
|
||||
OSR_TEST(DragDropDropElement, OSR_TEST_DRAG_DROP_DROP, 1.0f);
|
||||
OSR_TEST(DragDropDropElement2x, OSR_TEST_DRAG_DROP_DROP, 2.0f);
|
||||
OSR_TEST(IMESetComposition, OSR_TEST_IME_SET_COMPOSITION, 1.0f);
|
||||
OSR_TEST(IMESetComposition2x, OSR_TEST_IME_SET_COMPOSITION, 2.0f);
|
||||
OSR_TEST(IMECommitText, OSR_TEST_IME_COMMIT_TEXT, 1.0f);
|
||||
OSR_TEST(IMECommitText2x, OSR_TEST_IME_COMMIT_TEXT, 2.0f);
|
||||
OSR_TEST(IMEFinishComposition, OSR_TEST_IME_FINISH_COMPOSITION, 1.0f);
|
||||
OSR_TEST(IMEFinishComposition2x, OSR_TEST_IME_FINISH_COMPOSITION, 2.0f);
|
||||
OSR_TEST(IMECancelComposition, OSR_TEST_IME_CANCEL_COMPOSITION, 1.0f);
|
||||
OSR_TEST(IMECancelComposition2x, OSR_TEST_IME_CANCEL_COMPOSITION, 2.0f);
|
||||
|
|
|
@ -416,9 +416,10 @@ _simpletypes = {
|
|||
'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'],
|
||||
'cef_plugin_policy_t': ['cef_plugin_policy_t', 'PLUGIN_POLICY_ALLOW'],
|
||||
'CefCursorHandle' : ['cef_cursor_handle_t', 'kNullCursorHandle'],
|
||||
'CefCompositionUnderline' : ['cef_composition_underline_t',
|
||||
'CefCompositionUnderline()'],
|
||||
'CefEventHandle' : ['cef_event_handle_t', 'kNullEventHandle'],
|
||||
'CefWindowHandle' : ['cef_window_handle_t', 'kNullWindowHandle'],
|
||||
'CefTextInputContext' : ['cef_text_input_context_t' ,'NULL'],
|
||||
'CefPoint' : ['cef_point_t', 'CefPoint()'],
|
||||
'CefRect' : ['cef_rect_t', 'CefRect()'],
|
||||
'CefSize' : ['cef_size_t', 'CefSize()'],
|
||||
|
|
Loading…
Reference in New Issue