Skip to content

Commit 9fbcdd5

Browse files
committed
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 (a1fae21). 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.
1 parent 0796f51 commit 9fbcdd5

11 files changed

+164
-83
lines changed

src/mozc_version_template.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MAJOR=2
22
MINOR=17
3-
BUILD=2108
3+
BUILD=2109
44
REVISION=102
55
# NACL_DICTIONARY_VERSION is the target version of the system dictionary to be
66
# downloaded by NaCl Mozc.

src/renderer/unix/window_manager.cc

+8-16
Original file line numberDiff line numberDiff line change
@@ -101,27 +101,19 @@ Rect WindowManager::UpdateCandidateWindow(
101101
const Size new_window_size = candidate_window_->Update(candidates);
102102

103103
Point new_window_pos = candidate_window_->GetWindowPos();
104-
if (candidates.has_window_location()) {
105-
if (candidates.window_location() == commands::Candidates::CARET) {
106-
DCHECK(candidates.has_caret_rectangle());
107-
new_window_pos.x = candidates.caret_rectangle().x();
108-
new_window_pos.y = candidates.caret_rectangle().y()
109-
+ candidates.caret_rectangle().height();
110-
} else {
111-
DCHECK(candidates.has_composition_rectangle());
112-
new_window_pos.x = candidates.composition_rectangle().x();
113-
new_window_pos.y = candidates.composition_rectangle().y()
114-
+ candidates.composition_rectangle().height();
115-
}
104+
if (command.has_preedit_rectangle()) {
105+
new_window_pos.x = command.preedit_rectangle().left();
106+
new_window_pos.y = command.preedit_rectangle().bottom();
116107
}
117108

118109
const Rect working_area = GetMonitorRect(new_window_pos.x, new_window_pos.y);
119110
const Point alignment_base_point_in_local_window_coord(
120111
candidate_window_->GetCandidateColumnInClientCord().Left(), 0);
121-
const Rect caret_rect(candidates.caret_rectangle().x(),
122-
candidates.caret_rectangle().y(),
123-
candidates.caret_rectangle().width(),
124-
candidates.caret_rectangle().height());
112+
const auto &preedit_rect = command.preedit_rectangle();
113+
const Rect caret_rect(preedit_rect.left(),
114+
preedit_rect.top(),
115+
preedit_rect.right() - preedit_rect.left(),
116+
preedit_rect.bottom() - preedit_rect.top());
125117
// |caret_rect| is not always equal to preedit rect but can be an alternative
126118
// in terms of positional calculation, especially for vertical adjustment in
127119
// horizontal writing.

src/renderer/unix/window_manager_test.cc

+10-13
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,12 @@ TEST(WindowManagerTest, UpdateCandidateWindowTest) {
331331
const Size window_size(35, 45);
332332
const gint monitor = 0x7777;
333333

334-
candidates->set_window_location(commands::Candidates::CARET);
335334
const Rect caret_rect(16, 26, 2, 13);
336-
commands::Rectangle *rectangle = candidates->mutable_caret_rectangle();
337-
rectangle->set_x(caret_rect.Left());
338-
rectangle->set_y(caret_rect.Top());
339-
rectangle->set_width(caret_rect.Width());
340-
rectangle->set_height(caret_rect.Height());
335+
auto *rectangle = command.mutable_preedit_rectangle();
336+
rectangle->set_left(caret_rect.Left());
337+
rectangle->set_top(caret_rect.Top());
338+
rectangle->set_right(caret_rect.Right());
339+
rectangle->set_bottom(caret_rect.Bottom());
341340
const Point expected_window_position(
342341
caret_rect.Left() - client_cord_rect.Left(),
343342
caret_rect.Top() + caret_rect.Height());
@@ -400,14 +399,12 @@ TEST(WindowManagerTest, UpdateCandidateWindowTest) {
400399
const Size window_size(35, 45);
401400
const gint monitor = 0x7777;
402401

403-
candidates->set_window_location(commands::Candidates::COMPOSITION);
404402
const Rect comp_rect(16, 26, 2, 13);
405-
commands::Rectangle *rectangle
406-
= candidates->mutable_composition_rectangle();
407-
rectangle->set_x(comp_rect.Left());
408-
rectangle->set_y(comp_rect.Top());
409-
rectangle->set_width(comp_rect.Width());
410-
rectangle->set_height(comp_rect.Height());
403+
auto *rectangle = command.mutable_preedit_rectangle();
404+
rectangle->set_left(comp_rect.Left());
405+
rectangle->set_top(comp_rect.Top());
406+
rectangle->set_right(comp_rect.Right());
407+
rectangle->set_bottom(comp_rect.Bottom());
411408
const Point expected_window_position(
412409
comp_rect.Left() - client_cord_rect.Left(),
413410
comp_rect.Top() + comp_rect.Height());

src/unix/ibus/candidate_window_handler_interface.h

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ class CandidateWindowHandlerInterface {
4949
virtual void Update(IBusEngine *engine,
5050
const commands::Output &output) = 0;
5151

52+
// Updates candidate state. This function also shows or hides candidate window
53+
// based on the last |Update| call.
54+
virtual void UpdateCursorRect(IBusEngine *engine) = 0;
55+
5256
// Hides candidate window.
5357
virtual void Hide(IBusEngine *engine) = 0;
5458

src/unix/ibus/gtk_candidate_window_handler.cc

+24-8
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,25 @@ GtkCandidateWindowHandler::~GtkCandidateWindowHandler() {
5454
}
5555

5656
bool GtkCandidateWindowHandler::SendUpdateCommand(
57-
const commands::Output &output, bool visibility) const {
57+
IBusEngine *engine,
58+
const commands::Output &output,
59+
bool visibility) const {
5860
using commands::RendererCommand;
59-
6061
RendererCommand command;
61-
command.mutable_output()->CopyFrom(output);
62-
command.set_type(commands::RendererCommand::UPDATE);
62+
63+
*command.mutable_output() = output;
64+
command.set_type(RendererCommand::UPDATE);
6365
command.set_visible(visibility);
6466
RendererCommand::ApplicationInfo *appinfo
6567
= command.mutable_application_info();
6668

69+
auto *preedit_rectangle = command.mutable_preedit_rectangle();
70+
const auto &cursor_area = engine->cursor_area;
71+
preedit_rectangle->set_left(cursor_area.x);
72+
preedit_rectangle->set_top(cursor_area.y);
73+
preedit_rectangle->set_right(cursor_area.x + cursor_area.width);
74+
preedit_rectangle->set_bottom(cursor_area.y + cursor_area.height);
75+
6776
// Set pid
6877
static_assert(sizeof(::getpid()) <= sizeof(appinfo->process_id()),
6978
"|appinfo->process_id()| must have sufficient room.");
@@ -83,17 +92,24 @@ bool GtkCandidateWindowHandler::SendUpdateCommand(
8392

8493
void GtkCandidateWindowHandler::Update(IBusEngine *engine,
8594
const commands::Output &output) {
86-
last_update_output_->CopyFrom(output);
95+
*last_update_output_ = output;
96+
97+
UpdateCursorRect(engine);
98+
}
8799

88-
SendUpdateCommand(output, output.candidates().candidate_size() != 0);
100+
void GtkCandidateWindowHandler::UpdateCursorRect(IBusEngine *engine) {
101+
const bool has_candidates =
102+
last_update_output_->has_candidates() &&
103+
last_update_output_->candidates().candidate_size() > 0;
104+
SendUpdateCommand(engine, *last_update_output_, has_candidates);
89105
}
90106

91107
void GtkCandidateWindowHandler::Hide(IBusEngine *engine) {
92-
SendUpdateCommand(*(last_update_output_.get()), false);
108+
SendUpdateCommand(engine, *last_update_output_, false);
93109
}
94110

95111
void GtkCandidateWindowHandler::Show(IBusEngine *engine) {
96-
SendUpdateCommand(*(last_update_output_.get()), true);
112+
SendUpdateCommand(engine, *last_update_output_, true);
97113
}
98114

99115
void GtkCandidateWindowHandler::OnIBusCustomFontDescriptionChanged(

src/unix/ibus/gtk_candidate_window_handler.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
#include "unix/ibus/candidate_window_handler_interface.h"
3737

3838
namespace mozc {
39+
namespace commands {
40+
class RendererCommand;
41+
} // namespace commands
3942
namespace renderer {
4043
class RendererInterface;
4144
} // namespace renderer
@@ -48,6 +51,7 @@ class GtkCandidateWindowHandler : public CandidateWindowHandlerInterface {
4851
virtual ~GtkCandidateWindowHandler();
4952

5053
virtual void Update(IBusEngine *engine, const commands::Output &output);
54+
virtual void UpdateCursorRect(IBusEngine *engine);
5155
virtual void Hide(IBusEngine *engine);
5256
virtual void Show(IBusEngine *engine);
5357

@@ -58,7 +62,9 @@ class GtkCandidateWindowHandler : public CandidateWindowHandlerInterface {
5862
bool use_custom_font_description);
5963

6064
protected:
61-
bool SendUpdateCommand(const commands::Output &output, bool visibility) const;
65+
bool SendUpdateCommand(IBusEngine *engine,
66+
const commands::Output &output,
67+
bool visibility) const;
6268

6369
std::unique_ptr<renderer::RendererInterface> renderer_;
6470
std::unique_ptr<commands::Output> last_update_output_;

src/unix/ibus/gtk_candidate_window_handler_test.cc

+103-18
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
#include <unistd.h> // for getpid()
3333

34+
#include "base/coordinates.h"
3435
#include "protocol/renderer_command.pb.h"
3536
#include "renderer/renderer_mock.h"
3637
#include "testing/base/public/gmock.h"
@@ -110,6 +111,40 @@ MATCHER_P(VisibilityEq, visibility, "") {
110111
return true;
111112
}
112113

114+
MATCHER_P(PreeditRectangleEq, rect, "") {
115+
if (!arg.has_preedit_rectangle()) {
116+
*result_listener
117+
<< "RendererCommand::preedit_rectangle does not exist";
118+
return false;
119+
}
120+
const auto &actual_rect = arg.preedit_rectangle();
121+
if (rect.Left() != actual_rect.left()) {
122+
*result_listener << "left field does not match\n"
123+
<< " expected: " << rect.Left() << "\n"
124+
<< " actual: " << actual_rect.left();
125+
return false;
126+
}
127+
if (rect.Top() != actual_rect.top()) {
128+
*result_listener << "top field does not match\n"
129+
<< " expected: " << rect.Top() << "\n"
130+
<< " actual: " << actual_rect.top();
131+
return false;
132+
}
133+
if (rect.Right() != actual_rect.right()) {
134+
*result_listener << "right field does not match\n"
135+
<< " expected: " << rect.Right() << "\n"
136+
<< " actual: " << actual_rect.right();
137+
return false;
138+
}
139+
if (rect.Bottom() != actual_rect.bottom()) {
140+
*result_listener << "bottom field does not match\n"
141+
<< " expected: " << rect.Bottom() << "\n"
142+
<< " actual: " << actual_rect.bottom();
143+
return false;
144+
}
145+
return true;
146+
}
147+
113148
MATCHER_P(OutputEq, expected, "") {
114149
if (expected.Utf8DebugString() != arg.Utf8DebugString()) {
115150
*result_listener
@@ -129,47 +164,73 @@ MATCHER_P(OutputEq, expected, "") {
129164
} // namespace
130165

131166
TEST(GtkCandidateWindowHandlerTest, SendUpdateCommandTest) {
167+
const Rect kExpectedCursorArea(10, 20, 200, 100);
168+
169+
IBusEngine engine = {};
170+
engine.cursor_area.x = kExpectedCursorArea.Left();
171+
engine.cursor_area.y = kExpectedCursorArea.Top();
172+
engine.cursor_area.width = kExpectedCursorArea.Width();
173+
engine.cursor_area.height = kExpectedCursorArea.Height();
174+
132175
{
133176
SCOPED_TRACE("visibility check. false case");
134177
Output output;
135178
RendererMock *renderer_mock = new RendererMock();
136179
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
137180
renderer_mock);
138-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(false));
139-
gtk_candidate_window_handler.SendUpdateCommand(output, false);
181+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
182+
VisibilityEq(false),
183+
PreeditRectangleEq(kExpectedCursorArea));
184+
gtk_candidate_window_handler.SendUpdateCommand(&engine, output, false);
140185
}
141186
{
142187
SCOPED_TRACE("visibility check. true case");
143188
Output output;
144189
RendererMock *renderer_mock = new RendererMock();
145190
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
146191
renderer_mock);
147-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true));
148-
gtk_candidate_window_handler.SendUpdateCommand(output, true);
192+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
193+
VisibilityEq(true),
194+
PreeditRectangleEq(kExpectedCursorArea));
195+
gtk_candidate_window_handler.SendUpdateCommand(&engine, output, true);
149196
}
150197
{
151198
SCOPED_TRACE("return value check. false case.");
152199
Output output;
153200
RendererMock *renderer_mock = new RendererMock();
154201
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
155202
renderer_mock);
156-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true))
203+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
204+
VisibilityEq(true),
205+
PreeditRectangleEq(kExpectedCursorArea))
157206
.WillOnce(Return(false));
158-
EXPECT_FALSE(gtk_candidate_window_handler.SendUpdateCommand(output, true));
207+
EXPECT_FALSE(gtk_candidate_window_handler.SendUpdateCommand(
208+
&engine, output, true));
159209
}
160210
{
161211
SCOPED_TRACE("return value check. true case.");
162212
Output output;
163213
RendererMock *renderer_mock = new RendererMock();
164214
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
165215
renderer_mock);
166-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true))
216+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
217+
VisibilityEq(true),
218+
PreeditRectangleEq(kExpectedCursorArea))
167219
.WillOnce(Return(true));
168-
EXPECT_TRUE(gtk_candidate_window_handler.SendUpdateCommand(output, true));
220+
EXPECT_TRUE(gtk_candidate_window_handler.SendUpdateCommand(
221+
&engine, output, true));
169222
}
170223
}
171224

172225
TEST(GtkCandidateWindowHandlerTest, UpdateTest) {
226+
const Rect kExpectedCursorArea(10, 20, 200, 100);
227+
228+
IBusEngine engine = {};
229+
engine.cursor_area.x = kExpectedCursorArea.Left();
230+
engine.cursor_area.y = kExpectedCursorArea.Top();
231+
engine.cursor_area.width = kExpectedCursorArea.Width();
232+
engine.cursor_area.height = kExpectedCursorArea.Height();
233+
173234
const int sample_idx1 = 0;
174235
const int sample_idx2 = 1;
175236
const char *sample_candidate1 = "SAMPLE_CANDIDATE1";
@@ -180,8 +241,10 @@ TEST(GtkCandidateWindowHandlerTest, UpdateTest) {
180241
RendererMock *renderer_mock = new RendererMock();
181242
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
182243
renderer_mock);
183-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(false));
184-
gtk_candidate_window_handler.Update(NULL, output);
244+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
245+
VisibilityEq(false),
246+
PreeditRectangleEq(kExpectedCursorArea));
247+
gtk_candidate_window_handler.Update(&engine, output);
185248
}
186249
{
187250
SCOPED_TRACE("If there is at least one candidate, "
@@ -194,8 +257,10 @@ TEST(GtkCandidateWindowHandlerTest, UpdateTest) {
194257
RendererMock *renderer_mock = new RendererMock();
195258
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
196259
renderer_mock);
197-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true));
198-
gtk_candidate_window_handler.Update(NULL, output);
260+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
261+
VisibilityEq(true),
262+
PreeditRectangleEq(kExpectedCursorArea));
263+
gtk_candidate_window_handler.Update(&engine, output);
199264
}
200265
{
201266
SCOPED_TRACE("Update last updated output protobuf object.");
@@ -221,29 +286,49 @@ TEST(GtkCandidateWindowHandlerTest, UpdateTest) {
221286
Property(&RendererCommand::output,
222287
OutputEq(output2)))
223288
.WillOnce(Return(true));
224-
gtk_candidate_window_handler.Update(NULL, output1);
289+
gtk_candidate_window_handler.Update(&engine, output1);
225290
EXPECT_THAT(*(gtk_candidate_window_handler.last_update_output_.get()),
226291
OutputEq(output1));
227-
gtk_candidate_window_handler.Update(NULL, output2);
292+
gtk_candidate_window_handler.Update(&engine, output2);
228293
EXPECT_THAT(*(gtk_candidate_window_handler.last_update_output_.get()),
229294
OutputEq(output2));
230295
}
231296
}
232297

233298
TEST(GtkCandidateWindowHandlerTest, HideTest) {
299+
const Rect kExpectedCursorArea(10, 20, 200, 100);
300+
301+
IBusEngine engine = {};
302+
engine.cursor_area.x = kExpectedCursorArea.Left();
303+
engine.cursor_area.y = kExpectedCursorArea.Top();
304+
engine.cursor_area.width = kExpectedCursorArea.Width();
305+
engine.cursor_area.height = kExpectedCursorArea.Height();
306+
234307
RendererMock *renderer_mock = new RendererMock();
235308
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
236309
renderer_mock);
237-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(false));
238-
gtk_candidate_window_handler.Hide(NULL);
310+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
311+
VisibilityEq(false),
312+
PreeditRectangleEq(kExpectedCursorArea));
313+
gtk_candidate_window_handler.Hide(&engine);
239314
}
240315

241316
TEST(GtkCandidateWindowHandlerTest, ShowTest) {
317+
const Rect kExpectedCursorArea(10, 20, 200, 100);
318+
319+
IBusEngine engine = {};
320+
engine.cursor_area.x = kExpectedCursorArea.Left();
321+
engine.cursor_area.y = kExpectedCursorArea.Top();
322+
engine.cursor_area.width = kExpectedCursorArea.Width();
323+
engine.cursor_area.height = kExpectedCursorArea.Height();
324+
242325
RendererMock *renderer_mock = new RendererMock();
243326
TestableGtkCandidateWindowHandler gtk_candidate_window_handler(
244327
renderer_mock);
245-
EXPECT_CALL_EXEC_COMMAND(*renderer_mock, VisibilityEq(true));
246-
gtk_candidate_window_handler.Show(NULL);
328+
EXPECT_CALL_EXEC_COMMAND(*renderer_mock,
329+
VisibilityEq(true),
330+
PreeditRectangleEq(kExpectedCursorArea));
331+
gtk_candidate_window_handler.Show(&engine);
247332
}
248333

249334
} // namespace ibus

0 commit comments

Comments
 (0)