Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(249)

Side by Side Diff: ui/base/ime/android/cursor_anchor_info_controller.cc

Issue 699333003: Support InputMethodManager#updateCursorAnchorInfo for Android 5.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase and handle FocusedNodeChanged for performance optimization Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/base/ime/android/cursor_anchor_info_controller.h"
6
7 #include "base/android/build_info.h"
8 #include "base/strings/string_piece.h"
9 #include "base/strings/stringprintf.h"
10 #include "cc/output/compositor_frame_metadata.h"
11 #include "cc/output/viewport_selection_bound.h"
12 #include "ui/base/ime/android/cursor_anchor_info_builder.h"
13 #include "ui/base/ime/android/cursor_anchor_info_sender.h"
14
15 namespace ui {
16 namespace {
17
18 class CursorAnchorInfoControllerImpl final
19 : public CursorAnchorInfoController {
20 public:
21 CursorAnchorInfoControllerImpl(
22 CursorAnchorInfoSender* sender,
23 scoped_ptr<CursorAnchorInfoBuilder> builder);
24
25 ~CursorAnchorInfoControllerImpl() override;
26
27 void OnFocusedNodeChanged(bool is_editable_node) override;
28
29 void OnTextInputStateChanged(
30 const base::string16& text,
31 const gfx::Range& selection_range,
32 const gfx::Range& composition_range) override;
33
34 void OnCompositionRangeChanged(
35 const gfx::Range& range,
36 const std::vector<gfx::Rect>& character_bounds) override;
37
38 void OnFrameMetadataUpdated(
39 const cc::CompositorFrameMetadata& frame_metadata,
40 const gfx::Vector2d& view_offset) override;
41
42 bool OnRequestCursorUpdates(uint32 cursor_update_mode) override;
43
44 private:
45 void UpdateBuilder();
46 void ScheduleUpdate();
47 bool HasPendingUpdate() const;
48 void ClearPendingUpdate();
49 void SetMonitorMode(bool enabled);
50
51 // Doesn't own.
52 CursorAnchorInfoSender* cursor_anchor_info_sender_;
53 scoped_ptr<CursorAnchorInfoBuilder> cursor_anchor_info_builder_;
54
55 bool editable_;
56
57 base::string16 text_;
58 gfx::Range selection_range_;
59 gfx::Range composition_range_;
60 std::vector<gfx::Rect> character_bounds_;
61 gfx::Range character_bounds_range_;
62 bool has_insertion_marker_;
63 bool is_insertion_marker_visible_;
64 gfx::RectF insertion_marker_rect_;
65 bool has_coordinate_info_;
66 float scale_;
67 gfx::Vector2dF translation_;
68
69 bool has_pending_request_;
70 bool monitor_mode_enabled_;
71
72 DISALLOW_COPY_AND_ASSIGN(CursorAnchorInfoControllerImpl);
73 };
74
75 CursorAnchorInfoControllerImpl::CursorAnchorInfoControllerImpl(
76 CursorAnchorInfoSender* sender,
77 scoped_ptr<CursorAnchorInfoBuilder> builder)
78 : cursor_anchor_info_sender_(sender),
79 cursor_anchor_info_builder_(builder.Pass()),
80 editable_(false),
81 selection_range_(gfx::Range::InvalidRange()),
82 composition_range_(gfx::Range::InvalidRange()),
83 has_insertion_marker_(false),
84 is_insertion_marker_visible_(false),
85 has_coordinate_info_(false),
86 scale_(1.0f),
87 has_pending_request_(false),
88 monitor_mode_enabled_(false) {
89 }
90
91 CursorAnchorInfoControllerImpl::~CursorAnchorInfoControllerImpl() {
92 }
93
94 void CursorAnchorInfoControllerImpl::OnFocusedNodeChanged(
95 bool is_editable_node) {
96 editable_ = is_editable_node;
97 text_.clear();
98 selection_range_ = gfx::Range::InvalidRange();
99 composition_range_ = gfx::Range::InvalidRange();
100 character_bounds_.clear();
101 character_bounds_range_ = gfx::Range::InvalidRange();
102 has_insertion_marker_ = false;
103 is_insertion_marker_visible_= false;
104 insertion_marker_rect_.SetRect(0.0f, 0.0f, 0.0f, 0.0f);
105 has_coordinate_info_ = false;
106 scale_ = 1.0f;
107 translation_ = gfx::Vector2d();
108 }
109
110 void CursorAnchorInfoControllerImpl::OnTextInputStateChanged(
111 const base::string16& text,
112 const gfx::Range& selection_range,
113 const gfx::Range& composition_range) {
114 if (!editable_)
115 return;
116
117 if (monitor_mode_enabled_) {
118 const bool changed =
119 text_ != text
120 || selection_range_ != selection_range
121 || composition_range_ != composition_range;
122 if (changed)
123 has_pending_request_ = true;
124 }
125
126 text_ = text;
127 selection_range_ = selection_range;
128 composition_range_ = composition_range;
129 }
130
131 void CursorAnchorInfoControllerImpl::OnCompositionRangeChanged(
132 const gfx::Range& range,
133 const std::vector<gfx::Rect>& character_bounds) {
134 if (!editable_)
135 return;
136
137 if (monitor_mode_enabled_) {
138 const bool changed =
139 character_bounds_ != character_bounds
140 || character_bounds_range_ != range;
141 if (changed)
142 has_pending_request_ = true;
143 }
144
145 character_bounds_ = character_bounds;
146 character_bounds_range_ = range;
147 }
148
149 void CursorAnchorInfoControllerImpl::OnFrameMetadataUpdated(
150 const cc::CompositorFrameMetadata& frame_metadata,
151 const gfx::Vector2d& view_offset) {
152 if (!editable_)
153 return;
154
155 const auto& selection_start = frame_metadata.selection_start;
156
157 const float scale =
158 frame_metadata.device_scale_factor * frame_metadata.page_scale_factor;
159 const auto translation =
160 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, -scale)
jdduke (slow) 2015/01/12 16:33:44 Hmm, it looks like you're trying to synchronize th
yukawa 2015/01/12 17:08:30 I cannot check it right now as I'm in my home, but
161 + gfx::Vector2dF(view_offset.x(), view_offset.y())
162 + frame_metadata.location_bar_offset
163 + frame_metadata.location_bar_content_translation;
164
165 const bool has_insertion_marker =
166 selection_start.type == cc::SELECTION_BOUND_CENTER;
167 const bool is_insertion_marker_visible = selection_start.visible;
168 const gfx::PointF insertion_marker_origin = has_insertion_marker
169 ? selection_start.edge_top
170 : gfx::PointF(0.0f, 0.0f);
171 const float insertion_marker_height = has_insertion_marker
172 ? selection_start.edge_bottom.y() - selection_start.edge_top.y()
173 : 0.0f;
174 const gfx::SizeF insertion_marker_size(0.0f, insertion_marker_height);
175 const gfx::RectF insertion_marker_rect(insertion_marker_origin,
176 insertion_marker_size);
177 if (monitor_mode_enabled_) {
178 const bool changed =
179 !has_coordinate_info_
180 || scale_ != scale
181 || translation_ != translation
182 || has_insertion_marker_ != has_insertion_marker
183 || is_insertion_marker_visible_ != is_insertion_marker_visible
184 || insertion_marker_rect_ != insertion_marker_rect;
185 if (changed)
186 has_pending_request_ = true;
187 }
188
189 scale_ = scale;
190 translation_ = translation;
191 has_insertion_marker_ = has_insertion_marker;
192 is_insertion_marker_visible_ = is_insertion_marker_visible;
193 insertion_marker_rect_ = insertion_marker_rect;
194 has_coordinate_info_ = true;
195
196 if (cursor_anchor_info_sender_
197 && cursor_anchor_info_builder_
198 && HasPendingUpdate()) {
199 UpdateBuilder();
200 cursor_anchor_info_sender_->SendCursorAnchorInfo(
201 cursor_anchor_info_builder_.get());
202 ClearPendingUpdate();
203 }
204 }
205
206 bool CursorAnchorInfoControllerImpl::OnRequestCursorUpdates(
207 uint32 cursor_update_mode) {
208 if (!editable_)
209 return false;
210
211 // Should return false if at least one unknown bit is set, because we don't
212 // know what is requested by such a bit.
213 const static uint32 kKnownFlags =
214 kCursorUpdateModeImmediate | kCursorUpdateModeMonitor;
215 if ((cursor_update_mode & ~kKnownFlags) != 0)
216 return false;
217
218 const bool update_immediate = cursor_update_mode & kCursorUpdateModeImmediate;
219 const bool update_monitor = cursor_update_mode & kCursorUpdateModeMonitor;
220
221 SetMonitorMode(update_monitor);
222 if (update_immediate)
223 ScheduleUpdate();
224
225 if (cursor_anchor_info_sender_
226 && cursor_anchor_info_builder_
227 && HasPendingUpdate()) {
228 UpdateBuilder();
229 cursor_anchor_info_sender_->SendCursorAnchorInfo(
230 cursor_anchor_info_builder_.get());
231 ClearPendingUpdate();
232 }
233
234 return true;
235 }
236
237 void CursorAnchorInfoControllerImpl::ScheduleUpdate() {
238 has_pending_request_ = true;
239 }
240
241 bool CursorAnchorInfoControllerImpl::HasPendingUpdate() const {
242 return has_pending_request_ && has_coordinate_info_;
243 }
244
245 void CursorAnchorInfoControllerImpl::ClearPendingUpdate() {
246 has_pending_request_ = false;
247 }
248
249 void CursorAnchorInfoControllerImpl::SetMonitorMode(bool enabled) {
250 monitor_mode_enabled_ = enabled;
251 }
252
253 void CursorAnchorInfoControllerImpl::UpdateBuilder() {
254 DCHECK(HasPendingUpdate());
jdduke (slow) 2015/01/14 17:17:09 Is it necessary to blast all values to the builder
yukawa 2015/01/14 22:21:59 Basically we can, but one problem is that the resu
255
256 cursor_anchor_info_builder_->Reset();
257
258 if (selection_range_.IsValid())
259 cursor_anchor_info_builder_->SetSelectionRange(selection_range_);
260
261 if (composition_range_.IsValid()) {
262 const auto composition_text = base::StringPiece16(text_).substr(
263 composition_range_.start(),
264 composition_range_.length());
265 cursor_anchor_info_builder_->SetComposingText(
266 composition_range_.start(),
267 composition_text);
268 }
269 if (character_bounds_range_.IsValid()) {
270 const int index_offset = character_bounds_range_.start();
271 for (size_t i = 0; i < character_bounds_.size(); ++i) {
272 cursor_anchor_info_builder_->AddCharacterBounds(
273 index_offset + i,
274 character_bounds_[i],
275 CursorAnchorInfoBuilder::kFlagHasVisibleRegion);
276 }
277 }
278
279 if (has_insertion_marker_) {
280 const uint32 flags = is_insertion_marker_visible_
281 ? CursorAnchorInfoBuilder::kFlagHasVisibleRegion
282 : CursorAnchorInfoBuilder::kFlagHasInvisibleRegion;
283 cursor_anchor_info_builder_->SetInsertionMarkerLocation(
284 insertion_marker_rect_.x(),
285 insertion_marker_rect_.y(),
286 insertion_marker_rect_.bottom(),
287 insertion_marker_rect_.bottom(),
288 flags);
289 }
290
291 gfx::Matrix3F matrix = gfx::Matrix3F::Zeros();
292 matrix.set(scale_, 0.0f, translation_.x(),
293 0.0f, scale_, translation_.y(),
294 0.0f, 0.0f, 1.0f);
295 cursor_anchor_info_builder_->SetMatrix(matrix);
296 }
297
298 } // namespace
299
300 CursorAnchorInfoController::CursorAnchorInfoController() {
301 }
302
303 CursorAnchorInfoController::~CursorAnchorInfoController() {
304 }
305
306 // static
307 scoped_ptr<CursorAnchorInfoController>
308 CursorAnchorInfoController::Create(CursorAnchorInfoSender* sender) {
309 static bool use_cursor_anchor_info =
310 base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
311 if (!use_cursor_anchor_info)
312 return scoped_ptr<ui::CursorAnchorInfoController>(nullptr);
313
314 return scoped_ptr<ui::CursorAnchorInfoController>(
315 new ui::CursorAnchorInfoControllerImpl(
316 sender,
317 CursorAnchorInfoBuilder::Create()));
318 }
319
320 // static
321 scoped_ptr<CursorAnchorInfoController>
322 CursorAnchorInfoController::CreateForTest(
323 CursorAnchorInfoSender* sender,
324 scoped_ptr<ui::CursorAnchorInfoBuilder> builder) {
325 return scoped_ptr<CursorAnchorInfoController>(
326 new ui::CursorAnchorInfoControllerImpl(sender, builder.Pass()));
327 }
328
329 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698