Stop working around a limitation in GTK IM-module.
There is a long standing limitation in GTK IM-module that IMEs cannot retrieve
the screen coordinates of each composing character, which is definitely needed
to align suggestion window to the left edge of composing text.
In ibus-mozc, we have worked around this limitation by recording the cursor
rectangle in the mozc server rather and simulating the character screen
coordinates from those cursor rectangles since OSS Mozc 1.3.911.102
(a1fae21a95ffe8955a69c6c93255938db1f22e46).
This emulation has, however, never been perfect. Following issues are
actually edge cases of the emulation.
- #243: ibus predict window is shown at the previous cursor position
- https://bugzilla.mozilla.org/show_bug.cgi?id=1120851
Therefore we decided to remove the above emulation from ibus-mozc and live
in more robust but unsophisticated world instead. With this CL, the
suggestion window will show up just under the cursor location rather than
being aligned with composing text.
This clean-up also enables us to refactor mozc-server without bothering future
ibus-mozc maintainers because that emulation code that is implemented in
mozc-server. In subsequent CLs we can remove the emulation code without
breaking existing ibus-mozc client.
Closes #243.
BUG=#243
TEST=manually done on Ubuntu 14.04.
Yohei Yukawa
8 years ago
0 | 0 | MAJOR=2 |
1 | 1 | MINOR=17 |
2 | BUILD=2108 | |
2 | BUILD=2109 | |
3 | 3 | REVISION=102 |
4 | 4 | # NACL_DICTIONARY_VERSION is the target version of the system dictionary to be |
5 | 5 | # downloaded by NaCl Mozc. |
100 | 100 | const Size new_window_size = candidate_window_->Update(candidates); |
101 | 101 | |
102 | 102 | Point new_window_pos = candidate_window_->GetWindowPos(); |
103 | if (candidates.has_window_location()) { | |
104 | if (candidates.window_location() == commands::Candidates::CARET) { | |
105 | DCHECK(candidates.has_caret_rectangle()); | |
106 | new_window_pos.x = candidates.caret_rectangle().x(); | |
107 | new_window_pos.y = candidates.caret_rectangle().y() | |
108 | + candidates.caret_rectangle().height(); | |
109 | } else { | |
110 | DCHECK(candidates.has_composition_rectangle()); | |
111 | new_window_pos.x = candidates.composition_rectangle().x(); | |
112 | new_window_pos.y = candidates.composition_rectangle().y() | |
113 | + candidates.composition_rectangle().height(); | |
114 | } | |
103 | if (command.has_preedit_rectangle()) { | |
104 | new_window_pos.x = command.preedit_rectangle().left(); | |
105 | new_window_pos.y = command.preedit_rectangle().bottom(); | |
115 | 106 | } |
116 | 107 | |
117 | 108 | const Rect working_area = GetMonitorRect(new_window_pos.x, new_window_pos.y); |
118 | 109 | const Point alignment_base_point_in_local_window_coord( |
119 | 110 | candidate_window_->GetCandidateColumnInClientCord().Left(), 0); |
120 | const Rect caret_rect(candidates.caret_rectangle().x(), | |
121 | candidates.caret_rectangle().y(), | |
122 | candidates.caret_rectangle().width(), | |
123 | candidates.caret_rectangle().height()); | |
111 | const auto &preedit_rect = command.preedit_rectangle(); | |
112 | const Rect caret_rect(preedit_rect.left(), | |
113 | preedit_rect.top(), | |
114 | preedit_rect.right() - preedit_rect.left(), | |
115 | preedit_rect.bottom() - preedit_rect.top()); | |
124 | 116 | // |caret_rect| is not always equal to preedit rect but can be an alternative |
125 | 117 | // in terms of positional calculation, especially for vertical adjustment in |
126 | 118 | // horizontal writing. |
330 | 330 | const Size window_size(35, 45); |
331 | 331 | const gint monitor = 0x7777; |
332 | 332 | |
333 | candidates->set_window_location(commands::Candidates::CARET); | |
334 | 333 | const Rect caret_rect(16, 26, 2, 13); |
335 | commands::Rectangle *rectangle = candidates->mutable_caret_rectangle(); | |
336 | rectangle->set_x(caret_rect.Left()); | |
337 | rectangle->set_y(caret_rect.Top()); | |
338 | rectangle->set_width(caret_rect.Width()); | |
339 | rectangle->set_height(caret_rect.Height()); | |
334 | auto *rectangle = command.mutable_preedit_rectangle(); | |
335 | rectangle->set_left(caret_rect.Left()); | |
336 | rectangle->set_top(caret_rect.Top()); | |
337 | rectangle->set_right(caret_rect.Right()); | |
338 | rectangle->set_bottom(caret_rect.Bottom()); | |
340 | 339 | const Point expected_window_position( |
341 | 340 | caret_rect.Left() - client_cord_rect.Left(), |
342 | 341 | caret_rect.Top() + caret_rect.Height()); |
399 | 398 | const Size window_size(35, 45); |
400 | 399 | const gint monitor = 0x7777; |
401 | 400 | |
402 | candidates->set_window_location(commands::Candidates::COMPOSITION); | |
403 | 401 | const Rect comp_rect(16, 26, 2, 13); |
404 | commands::Rectangle *rectangle | |
405 | = candidates->mutable_composition_rectangle(); | |
406 | rectangle->set_x(comp_rect.Left()); | |
407 | rectangle->set_y(comp_rect.Top()); | |
408 | rectangle->set_width(comp_rect.Width()); | |
409 | rectangle->set_height(comp_rect.Height()); | |
402 | auto *rectangle = command.mutable_preedit_rectangle(); | |
403 | rectangle->set_left(comp_rect.Left()); | |
404 | rectangle->set_top(comp_rect.Top()); | |
405 | rectangle->set_right(comp_rect.Right()); | |
406 | rectangle->set_bottom(comp_rect.Bottom()); | |
410 | 407 | const Point expected_window_position( |
411 | 408 | comp_rect.Left() - client_cord_rect.Left(), |
412 | 409 | comp_rect.Top() + comp_rect.Height()); |
48 | 48 | virtual void Update(IBusEngine *engine, |
49 | 49 | const commands::Output &output) = 0; |
50 | 50 | |
51 | // Updates candidate state. This function also shows or hides candidate window | |
52 | // based on the last |Update| call. | |
53 | virtual void UpdateCursorRect(IBusEngine *engine) = 0; | |
54 | ||
51 | 55 | // Hides candidate window. |
52 | 56 | virtual void Hide(IBusEngine *engine) = 0; |
53 | 57 |
53 | 53 | } |
54 | 54 | |
55 | 55 | bool GtkCandidateWindowHandler::SendUpdateCommand( |
56 | const commands::Output &output, bool visibility) const { | |
56 | IBusEngine *engine, | |
57 | const commands::Output &output, | |
58 | bool visibility) const { | |
57 | 59 | using commands::RendererCommand; |
60 | RendererCommand command; | |
58 | 61 | |
59 | RendererCommand command; | |
60 | command.mutable_output()->CopyFrom(output); | |
61 | command.set_type(commands::RendererCommand::UPDATE); | |
62 | *command.mutable_output() = output; | |
63 | command.set_type(RendererCommand::UPDATE); | |
62 | 64 | command.set_visible(visibility); |
63 | 65 | RendererCommand::ApplicationInfo *appinfo |
64 | 66 | = command.mutable_application_info(); |
67 | ||
68 | auto *preedit_rectangle = command.mutable_preedit_rectangle(); | |
69 | const auto &cursor_area = engine->cursor_area; | |
70 | preedit_rectangle->set_left(cursor_area.x); | |
71 | preedit_rectangle->set_top(cursor_area.y); | |
72 | preedit_rectangle->set_right(cursor_area.x + cursor_area.width); | |
73 | preedit_rectangle->set_bottom(cursor_area.y + cursor_area.height); | |
65 | 74 | |
66 | 75 | // Set pid |
67 | 76 | static_assert(sizeof(::getpid()) <= sizeof(appinfo->process_id()), |
82 | 91 | |
83 | 92 | void GtkCandidateWindowHandler::Update(IBusEngine *engine, |
84 | 93 | const commands::Output &output) { |
85 | last_update_output_->CopyFrom(output); | |
94 | *last_update_output_ = output; | |
86 | 95 | |
87 | SendUpdateCommand(output, output.candidates().candidate_size() != 0); | |
96 | UpdateCursorRect(engine); | |
97 | } | |
98 | ||
99 | void GtkCandidateWindowHandler::UpdateCursorRect(IBusEngine *engine) { | |
100 | const bool has_candidates = | |
101 | last_update_output_->has_candidates() && | |
102 | last_update_output_->candidates().candidate_size() > 0; | |
103 | SendUpdateCommand(engine, *last_update_output_, has_candidates); | |
88 | 104 | } |
89 | 105 | |
90 | 106 | void GtkCandidateWindowHandler::Hide(IBusEngine *engine) { |
91 | SendUpdateCommand(*(last_update_output_.get()), false); | |
107 | SendUpdateCommand(engine, *last_update_output_, false); | |
92 | 108 | } |
93 | 109 | |
94 | 110 | void GtkCandidateWindowHandler::Show(IBusEngine *engine) { |
95 | SendUpdateCommand(*(last_update_output_.get()), true); | |
111 | SendUpdateCommand(engine, *last_update_output_, true); | |
96 | 112 | } |
97 | 113 | |
98 | 114 | void GtkCandidateWindowHandler::OnIBusCustomFontDescriptionChanged( |
35 | 35 | #include "unix/ibus/candidate_window_handler_interface.h" |
36 | 36 | |
37 | 37 | namespace mozc { |
38 | namespace commands { | |
39 | class RendererCommand; | |
40 | } // namespace commands | |
38 | 41 | namespace renderer { |
39 | 42 | class RendererInterface; |
40 | 43 | } // namespace renderer |
47 | 50 | virtual ~GtkCandidateWindowHandler(); |
48 | 51 | |
49 | 52 | virtual void Update(IBusEngine *engine, const commands::Output &output); |
53 | virtual void UpdateCursorRect(IBusEngine *engine); | |
50 | 54 | virtual void Hide(IBusEngine *engine); |
51 | 55 | virtual void Show(IBusEngine *engine); |
52 | 56 | |
57 | 61 | bool use_custom_font_description); |
58 | 62 | |
59 | 63 | protected: |
60 | bool SendUpdateCommand(const commands::Output &output, bool visibility) const; | |
64 | bool SendUpdateCommand(IBusEngine *engine, | |
65 | const commands::Output &output, | |
66 | bool visibility) const; | |
61 | 67 | |
62 | 68 | std::unique_ptr<renderer::RendererInterface> renderer_; |
63 | 69 | std::unique_ptr<commands::Output> last_update_output_; |
30 | 30 | |
31 | 31 | #include <unistd.h> // for getpid() |
32 | 32 | |
33 | #include "base/coordinates.h" | |
33 | 34 | #include "protocol/renderer_command.pb.h" |
34 | 35 | #include "renderer/renderer_mock.h" |
35 | 36 | #include "testing/base/public/gmock.h" |
109 | 110 | return true; |
110 | 111 | } |
111 | 112 | |
113 | MATCHER_P(PreeditRectangleEq, rect, "") { | |
114 | if (!arg.has_preedit_rectangle()) { | |
115 | *result_listener | |
116 | << "RendererCommand::preedit_rectangle does not exist"; | |
117 | return false; | |
118 | } | |
119 | const auto &actual_rect = arg.preedit_rectangle(); | |
120 | if (rect.Left() != actual_rect.left()) { | |
121 | *result_listener << "left field does not match\n" | |
122 | << " expected: " << rect.Left() << "\n" | |
123 | << " actual: " << actual_rect.left(); | |
124 | return false; | |
125 | } | |
126 | if (rect.Top() != actual_rect.top()) { | |
127 | *result_listener << "top field does not match\n" | |
128 | << " expected: " << rect.Top() << "\n" | |
129 | << " actual: " << actual_rect.top(); | |
130 | return false; | |
131 | } | |
132 | if (rect.Right() != actual_rect.right()) { | |
133 | *result_listener << "right field does not match\n" | |
134 | << " expected: " << rect.Right() << "\n" | |
135 | << " actual: " << actual_rect.right(); | |
136 | return false; | |
137 | } | |
138 | if (rect.Bottom() != actual_rect.bottom()) { | |
139 | *result_listener << "bottom field does not match\n" | |
140 | << " expected: " << rect.Bottom() << "\n" | |
141 | << " actual: " << actual_rect.bottom(); | |
142 | return false; | |
143 | } | |
144 | return true; | |
145 | } | |
146 | ||
112 | 147 | MATCHER_P(OutputEq, expected, "") { |
113 | 148 | if (expected.Utf8DebugString() != arg.Utf8DebugString()) { |
114 | 149 | *result_listener |
128 | 163 | } // namespace |
129 | 164 | |
130 | 165 | TEST(GtkCandidateWindowHandlerTest, SendUpdateCommandTest) { |
166 | const Rect kExpectedCursorArea(10, 20, 200, 100); | |
167 | ||
168 | IBusEngine engine = {}; | |
169 | engine.cursor_area.x = kExpectedCursorArea.Left(); | |
170 | engine.cursor_area.y = kExpectedCursorArea.Top(); | |
171 | engine.cursor_area.width = kExpectedCursorArea.Width(); | |
172 | engine.cursor_area.height = kExpectedCursorArea.Height(); | |
173 | ||
131 | 174 | { |
132 | 175 | SCOPED_TRACE("visibility check. false case"); |
133 | 176 | Output output; |
134 | 177 | RendererMock *renderer_mock = new RendererMock(); |
135 | 178 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
136 | 179 | renderer_mock); |
137 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(false)); | |
138 | gtk_candidate_window_handler.SendUpdateCommand(output, false); | |
180 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
181 | VisibilityEq(false), | |
182 | PreeditRectangleEq(kExpectedCursorArea)); | |
183 | gtk_candidate_window_handler.SendUpdateCommand(&engine, output, false); | |
139 | 184 | } |
140 | 185 | { |
141 | 186 | SCOPED_TRACE("visibility check. true case"); |
143 | 188 | RendererMock *renderer_mock = new RendererMock(); |
144 | 189 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
145 | 190 | renderer_mock); |
146 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true)); | |
147 | gtk_candidate_window_handler.SendUpdateCommand(output, true); | |
191 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
192 | VisibilityEq(true), | |
193 | PreeditRectangleEq(kExpectedCursorArea)); | |
194 | gtk_candidate_window_handler.SendUpdateCommand(&engine, output, true); | |
148 | 195 | } |
149 | 196 | { |
150 | 197 | SCOPED_TRACE("return value check. false case."); |
152 | 199 | RendererMock *renderer_mock = new RendererMock(); |
153 | 200 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
154 | 201 | renderer_mock); |
155 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true)) | |
202 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
203 | VisibilityEq(true), | |
204 | PreeditRectangleEq(kExpectedCursorArea)) | |
156 | 205 | .WillOnce(Return(false)); |
157 | EXPECT_FALSE(gtk_candidate_window_handler.SendUpdateCommand(output, true)); | |
206 | EXPECT_FALSE(gtk_candidate_window_handler.SendUpdateCommand( | |
207 | &engine, output, true)); | |
158 | 208 | } |
159 | 209 | { |
160 | 210 | SCOPED_TRACE("return value check. true case."); |
162 | 212 | RendererMock *renderer_mock = new RendererMock(); |
163 | 213 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
164 | 214 | renderer_mock); |
165 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true)) | |
215 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
216 | VisibilityEq(true), | |
217 | PreeditRectangleEq(kExpectedCursorArea)) | |
166 | 218 | .WillOnce(Return(true)); |
167 | EXPECT_TRUE(gtk_candidate_window_handler.SendUpdateCommand(output, true)); | |
219 | EXPECT_TRUE(gtk_candidate_window_handler.SendUpdateCommand( | |
220 | &engine, output, true)); | |
168 | 221 | } |
169 | 222 | } |
170 | 223 | |
171 | 224 | TEST(GtkCandidateWindowHandlerTest, UpdateTest) { |
225 | const Rect kExpectedCursorArea(10, 20, 200, 100); | |
226 | ||
227 | IBusEngine engine = {}; | |
228 | engine.cursor_area.x = kExpectedCursorArea.Left(); | |
229 | engine.cursor_area.y = kExpectedCursorArea.Top(); | |
230 | engine.cursor_area.width = kExpectedCursorArea.Width(); | |
231 | engine.cursor_area.height = kExpectedCursorArea.Height(); | |
232 | ||
172 | 233 | const int sample_idx1 = 0; |
173 | 234 | const int sample_idx2 = 1; |
174 | 235 | const char *sample_candidate1 = "SAMPLE_CANDIDATE1"; |
179 | 240 | RendererMock *renderer_mock = new RendererMock(); |
180 | 241 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
181 | 242 | renderer_mock); |
182 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(false)); | |
183 | gtk_candidate_window_handler.Update(NULL, output); | |
243 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
244 | VisibilityEq(false), | |
245 | PreeditRectangleEq(kExpectedCursorArea)); | |
246 | gtk_candidate_window_handler.Update(&engine, output); | |
184 | 247 | } |
185 | 248 | { |
186 | 249 | SCOPED_TRACE("If there is at least one candidate, " |
193 | 256 | RendererMock *renderer_mock = new RendererMock(); |
194 | 257 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
195 | 258 | renderer_mock); |
196 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true)); | |
197 | gtk_candidate_window_handler.Update(NULL, output); | |
259 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
260 | VisibilityEq(true), | |
261 | PreeditRectangleEq(kExpectedCursorArea)); | |
262 | gtk_candidate_window_handler.Update(&engine, output); | |
198 | 263 | } |
199 | 264 | { |
200 | 265 | SCOPED_TRACE("Update last updated output protobuf object."); |
220 | 285 | Property(&RendererCommand::output, |
221 | 286 | OutputEq(output2))) |
222 | 287 | .WillOnce(Return(true)); |
223 | gtk_candidate_window_handler.Update(NULL, output1); | |
288 | gtk_candidate_window_handler.Update(&engine, output1); | |
224 | 289 | EXPECT_THAT(*(gtk_candidate_window_handler.last_update_output_.get()), |
225 | 290 | OutputEq(output1)); |
226 | gtk_candidate_window_handler.Update(NULL, output2); | |
291 | gtk_candidate_window_handler.Update(&engine, output2); | |
227 | 292 | EXPECT_THAT(*(gtk_candidate_window_handler.last_update_output_.get()), |
228 | 293 | OutputEq(output2)); |
229 | 294 | } |
230 | 295 | } |
231 | 296 | |
232 | 297 | TEST(GtkCandidateWindowHandlerTest, HideTest) { |
298 | const Rect kExpectedCursorArea(10, 20, 200, 100); | |
299 | ||
300 | IBusEngine engine = {}; | |
301 | engine.cursor_area.x = kExpectedCursorArea.Left(); | |
302 | engine.cursor_area.y = kExpectedCursorArea.Top(); | |
303 | engine.cursor_area.width = kExpectedCursorArea.Width(); | |
304 | engine.cursor_area.height = kExpectedCursorArea.Height(); | |
305 | ||
233 | 306 | RendererMock *renderer_mock = new RendererMock(); |
234 | 307 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
235 | 308 | renderer_mock); |
236 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(false)); | |
237 | gtk_candidate_window_handler.Hide(NULL); | |
309 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
310 | VisibilityEq(false), | |
311 | PreeditRectangleEq(kExpectedCursorArea)); | |
312 | gtk_candidate_window_handler.Hide(&engine); | |
238 | 313 | } |
239 | 314 | |
240 | 315 | TEST(GtkCandidateWindowHandlerTest, ShowTest) { |
316 | const Rect kExpectedCursorArea(10, 20, 200, 100); | |
317 | ||
318 | IBusEngine engine = {}; | |
319 | engine.cursor_area.x = kExpectedCursorArea.Left(); | |
320 | engine.cursor_area.y = kExpectedCursorArea.Top(); | |
321 | engine.cursor_area.width = kExpectedCursorArea.Width(); | |
322 | engine.cursor_area.height = kExpectedCursorArea.Height(); | |
323 | ||
241 | 324 | RendererMock *renderer_mock = new RendererMock(); |
242 | 325 | TestableGtkCandidateWindowHandler gtk_candidate_window_handler( |
243 | 326 | renderer_mock); |
244 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true)); | |
245 | gtk_candidate_window_handler.Show(NULL); | |
327 | EXPECT_CALL_EXEC_COMMAND(*renderer_mock, | |
328 | VisibilityEq(true), | |
329 | PreeditRectangleEq(kExpectedCursorArea)); | |
330 | gtk_candidate_window_handler.Show(&engine); | |
246 | 331 | } |
247 | 332 | |
248 | 333 | } // namespace ibus |
91 | 91 | UpdateAuxiliaryText(engine, output); |
92 | 92 | } |
93 | 93 | |
94 | void IBusCandidateWindowHandler::UpdateCursorRect(IBusEngine *engine) { | |
95 | // Nothing to do because IBus takes care of where to show its candidate | |
96 | // window. | |
97 | } | |
98 | ||
94 | 99 | void IBusCandidateWindowHandler::Hide(IBusEngine *engine) { |
95 | 100 | ibus_engine_hide_lookup_table(engine); |
96 | 101 | ibus_engine_hide_auxiliary_text(engine); |
40 | 40 | virtual ~IBusCandidateWindowHandler(); |
41 | 41 | |
42 | 42 | virtual void Update(IBusEngine *engine, const commands::Output &output); |
43 | virtual void UpdateCursorRect(IBusEngine *engine); | |
43 | 44 | virtual void Hide(IBusEngine *engine); |
44 | 45 | virtual void Show(IBusEngine *engine); |
45 | 46 |
363 | 363 | return FALSE; |
364 | 364 | } |
365 | 365 | |
366 | // Send current caret location to mozc_server to manage suggest window | |
367 | // position. | |
368 | // TODO(nona): Merge SendKey event to reduce IPC cost. | |
369 | // TODO(nona): Add a unit test against b/6209562. | |
370 | SendCaretLocation(engine->cursor_area.x, | |
371 | engine->cursor_area.y, | |
372 | engine->cursor_area.width, | |
373 | engine->cursor_area.height); | |
374 | ||
375 | 366 | // TODO(yusukes): use |layout| in IBusEngineDesc if possible. |
376 | 367 | const bool layout_is_jp = |
377 | 368 | !g_strcmp0(ibus_engine_get_name(engine), "mozc-jp"); |
447 | 438 | gint y, |
448 | 439 | gint w, |
449 | 440 | gint h) { |
450 | // Do nothing | |
441 | GetCandidateWindowHandler(engine)->UpdateCursorRect(engine); | |
451 | 442 | } |
452 | 443 | |
453 | 444 | void MozcEngine::SetContentType(IBusEngine *engine, |
789 | 780 | #endif // not ENABLE_GTK_RENDERER |
790 | 781 | } |
791 | 782 | |
792 | void MozcEngine::SendCaretLocation(uint32 x, uint32 y, uint32 width, | |
793 | uint32 height) { | |
794 | commands::Output output; | |
795 | commands::SessionCommand command; | |
796 | command.set_type(commands::SessionCommand::SEND_CARET_LOCATION); | |
797 | commands::Rectangle *caret_rectangle = command.mutable_caret_rectangle(); | |
798 | caret_rectangle->set_x(x); | |
799 | caret_rectangle->set_y(y); | |
800 | caret_rectangle->set_width(width); | |
801 | caret_rectangle->set_height(height); | |
802 | client_->SendCommand(command, &output); | |
803 | } | |
804 | ||
805 | 783 | } // namespace ibus |
806 | 784 | } // namespace mozc |
154 | 154 | // message, then hides a preedit string and the candidate window. |
155 | 155 | void RevertSession(IBusEngine *engine); |
156 | 156 | |
157 | // Sends current caret location to mozc_server. | |
158 | void SendCaretLocation(uint32 x, uint32 y, uint32 width, uint32 height); | |
159 | ||
160 | 157 | CandidateWindowHandlerInterface *GetCandidateWindowHandler( |
161 | 158 | IBusEngine *engine); |
162 | 159 |