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

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

Issue 643193003: Support InputMethodManager#updateCursorAnchorInfo for Android 5.0 (C++ version) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Send ImeCompositionRangeChanged only when necessary Created 5 years, 10 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 #include "ui/base/ui_base_switches_util.h"
15
16 namespace ui {
17 namespace {
18
19 class CursorAnchorInfoControllerImpl final
20 : public CursorAnchorInfoController {
21 public:
22 CursorAnchorInfoControllerImpl(
23 CursorAnchorInfoSender* sender,
24 scoped_ptr<CursorAnchorInfoBuilder> builder);
25
26 ~CursorAnchorInfoControllerImpl() override;
27
28 void OnFocusedNodeChanged(bool is_editable_node) override;
29
30 void OnTextInputStateChanged(
31 const base::string16& text,
32 const gfx::Range& selection_range,
33 const gfx::Range& composition_range) override;
34
35 void OnCompositionRangeChanged(
36 const gfx::Range& range,
37 const std::vector<gfx::Rect>& character_bounds) override;
38
39 void OnFrameMetadataUpdated(
40 const cc::CompositorFrameMetadata& frame_metadata,
41 const gfx::Vector2d& view_offset) override;
42
43 bool OnRequestCursorUpdates(uint32 cursor_update_mode) override;
44
45 private:
46 void UpdateBuilder();
47 void ScheduleUpdate();
48 bool HasPendingUpdate() const;
49 void ClearPendingUpdate();
50 void SetMonitorMode(bool enabled);
51
52 // Doesn't own.
53 CursorAnchorInfoSender* cursor_anchor_info_sender_;
54 scoped_ptr<CursorAnchorInfoBuilder> cursor_anchor_info_builder_;
55
56 bool editable_;
57
58 base::string16 text_;
59 gfx::Range selection_range_;
60 gfx::Range composition_range_;
61 std::vector<gfx::Rect> character_bounds_;
62 gfx::Range character_bounds_range_;
63 bool has_insertion_marker_;
64 bool is_insertion_marker_visible_;
65 gfx::RectF insertion_marker_rect_;
66 bool has_coordinate_info_;
67 float scale_;
68 gfx::Vector2dF translation_;
69
70 bool has_pending_request_;
71 bool monitor_mode_enabled_;
72
73 DISALLOW_COPY_AND_ASSIGN(CursorAnchorInfoControllerImpl);
74 };
75
76 CursorAnchorInfoControllerImpl::CursorAnchorInfoControllerImpl(
77 CursorAnchorInfoSender* sender,
78 scoped_ptr<CursorAnchorInfoBuilder> builder)
79 : cursor_anchor_info_sender_(sender),
80 cursor_anchor_info_builder_(builder.Pass()),
81 editable_(false),
82 selection_range_(gfx::Range::InvalidRange()),
83 composition_range_(gfx::Range::InvalidRange()),
84 has_insertion_marker_(false),
85 is_insertion_marker_visible_(false),
86 has_coordinate_info_(false),
87 scale_(1.0f),
88 has_pending_request_(false),
89 monitor_mode_enabled_(false) {
90 }
91
92 CursorAnchorInfoControllerImpl::~CursorAnchorInfoControllerImpl() {
93 }
94
95 void CursorAnchorInfoControllerImpl::OnFocusedNodeChanged(
96 bool is_editable_node) {
97 editable_ = is_editable_node;
98 text_.clear();
99 selection_range_ = gfx::Range::InvalidRange();
100 composition_range_ = gfx::Range::InvalidRange();
101 character_bounds_.clear();
102 character_bounds_range_ = gfx::Range::InvalidRange();
103 has_insertion_marker_ = false;
104 is_insertion_marker_visible_= false;
105 insertion_marker_rect_.SetRect(0.0f, 0.0f, 0.0f, 0.0f);
106 has_coordinate_info_ = false;
107 scale_ = 1.0f;
108 translation_ = gfx::Vector2d();
109 }
110
111 void CursorAnchorInfoControllerImpl::OnTextInputStateChanged(
112 const base::string16& text,
113 const gfx::Range& selection_range,
114 const gfx::Range& composition_range) {
115 if (!editable_)
116 return;
117
118 if (monitor_mode_enabled_) {
119 const bool changed =
120 text_ != text
121 || selection_range_ != selection_range
122 || composition_range_ != composition_range;
123 if (changed)
124 has_pending_request_ = true;
125 }
126
127 text_ = text;
128 selection_range_ = selection_range;
129 composition_range_ = composition_range;
130 }
131
132 void CursorAnchorInfoControllerImpl::OnCompositionRangeChanged(
133 const gfx::Range& range,
134 const std::vector<gfx::Rect>& character_bounds) {
135 if (!editable_)
136 return;
137
138 if (monitor_mode_enabled_) {
139 const bool changed =
140 character_bounds_ != character_bounds
141 || character_bounds_range_ != range;
142 if (changed)
143 has_pending_request_ = true;
144 }
145
146 character_bounds_ = character_bounds;
147 character_bounds_range_ = range;
148 }
149
150 void CursorAnchorInfoControllerImpl::OnFrameMetadataUpdated(
151 const cc::CompositorFrameMetadata& frame_metadata,
152 const gfx::Vector2d& view_offset) {
153 if (!editable_)
154 return;
155
156 const auto& selection_start = frame_metadata.selection_start;
157
158 const float scale =
159 frame_metadata.device_scale_factor * frame_metadata.page_scale_factor;
160 const auto translation =
161 gfx::ScaleVector2d(frame_metadata.root_scroll_offset, -scale)
162 + gfx::Vector2dF(view_offset.x(), view_offset.y())
163 + frame_metadata.location_bar_offset
164 + frame_metadata.location_bar_content_translation;
165
166 const bool has_insertion_marker =
167 selection_start.type == cc::SELECTION_BOUND_CENTER;
168 const bool is_insertion_marker_visible = selection_start.visible;
169 const gfx::PointF insertion_marker_origin = has_insertion_marker
170 ? selection_start.edge_top
171 : gfx::PointF(0.0f, 0.0f);
172 const float insertion_marker_height = has_insertion_marker
173 ? selection_start.edge_bottom.y() - selection_start.edge_top.y()
174 : 0.0f;
175 const gfx::SizeF insertion_marker_size(0.0f, insertion_marker_height);
176 const gfx::RectF insertion_marker_rect(insertion_marker_origin,
177 insertion_marker_size);
178 if (monitor_mode_enabled_) {
179 const bool changed =
180 !has_coordinate_info_
181 || scale_ != scale
182 || translation_ != translation
183 || has_insertion_marker_ != has_insertion_marker
184 || is_insertion_marker_visible_ != is_insertion_marker_visible
185 || insertion_marker_rect_ != insertion_marker_rect;
186 if (changed)
187 has_pending_request_ = true;
188 }
189
190 scale_ = scale;
191 translation_ = translation;
192 has_insertion_marker_ = has_insertion_marker;
193 is_insertion_marker_visible_ = is_insertion_marker_visible;
194 insertion_marker_rect_ = insertion_marker_rect;
195 has_coordinate_info_ = true;
196
197 if (cursor_anchor_info_sender_
198 && cursor_anchor_info_builder_
199 && HasPendingUpdate()) {
200 UpdateBuilder();
201 cursor_anchor_info_sender_->SendCursorAnchorInfo(
202 cursor_anchor_info_builder_.get());
203 ClearPendingUpdate();
204 }
205 }
206
207 bool CursorAnchorInfoControllerImpl::OnRequestCursorUpdates(
208 uint32 cursor_update_mode) {
209 if (!editable_)
210 return false;
211
212 // Should return false if at least one unknown bit is set, because we don't
213 // know what is requested by such a bit.
214 const static uint32 kKnownFlags =
215 kCursorUpdateModeImmediate | kCursorUpdateModeMonitor;
216 if ((cursor_update_mode & ~kKnownFlags) != 0)
217 return false;
218
219 const bool update_immediate = cursor_update_mode & kCursorUpdateModeImmediate;
220 const bool update_monitor = cursor_update_mode & kCursorUpdateModeMonitor;
221
222 SetMonitorMode(update_monitor);
223 if (update_immediate)
224 ScheduleUpdate();
225
226 if (cursor_anchor_info_sender_
227 && cursor_anchor_info_builder_
228 && HasPendingUpdate()) {
229 UpdateBuilder();
230 cursor_anchor_info_sender_->SendCursorAnchorInfo(
231 cursor_anchor_info_builder_.get());
232 ClearPendingUpdate();
233 }
234
235 return true;
236 }
237
238 void CursorAnchorInfoControllerImpl::ScheduleUpdate() {
239 has_pending_request_ = true;
240 }
241
242 bool CursorAnchorInfoControllerImpl::HasPendingUpdate() const {
243 return has_pending_request_ && has_coordinate_info_;
244 }
245
246 void CursorAnchorInfoControllerImpl::ClearPendingUpdate() {
247 has_pending_request_ = false;
248 }
249
250 void CursorAnchorInfoControllerImpl::SetMonitorMode(bool enabled) {
251 monitor_mode_enabled_ = enabled;
252 }
253
254 void CursorAnchorInfoControllerImpl::UpdateBuilder() {
255 DCHECK(HasPendingUpdate());
256
257 cursor_anchor_info_builder_->Reset();
258
259 if (selection_range_.IsValid())
260 cursor_anchor_info_builder_->SetSelectionRange(selection_range_);
261
262 if (composition_range_.IsValid()) {
263 const auto composition_text = base::StringPiece16(text_).substr(
264 composition_range_.start(),
265 composition_range_.length());
266 cursor_anchor_info_builder_->SetComposingText(
267 composition_range_.start(),
268 composition_text);
269 }
270 if (character_bounds_range_.IsValid()) {
271 const int index_offset = character_bounds_range_.start();
272 for (size_t i = 0; i < character_bounds_.size(); ++i) {
273 cursor_anchor_info_builder_->AddCharacterBounds(
274 index_offset + i,
275 character_bounds_[i],
276 CursorAnchorInfoBuilder::kFlagHasVisibleRegion);
277 }
278 }
279
280 if (has_insertion_marker_) {
281 const uint32 flags = is_insertion_marker_visible_
282 ? CursorAnchorInfoBuilder::kFlagHasVisibleRegion
283 : CursorAnchorInfoBuilder::kFlagHasInvisibleRegion;
284 cursor_anchor_info_builder_->SetInsertionMarkerLocation(
285 insertion_marker_rect_.x(),
286 insertion_marker_rect_.y(),
287 insertion_marker_rect_.bottom(),
288 insertion_marker_rect_.bottom(),
289 flags);
290 }
291 cursor_anchor_info_builder_->SetScaleAndTranslate(scale_,
292 translation_);
293 }
294
295 } // namespace
296
297 CursorAnchorInfoController::CursorAnchorInfoController() {
298 }
299
300 CursorAnchorInfoController::~CursorAnchorInfoController() {
301 }
302
303 // static
304 scoped_ptr<CursorAnchorInfoController>
305 CursorAnchorInfoController::Create(CursorAnchorInfoSender* sender) {
306 if (!switches::IsCursorAnchorInfoEnabled())
307 return scoped_ptr<ui::CursorAnchorInfoController>();
308
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>();
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
« no previous file with comments | « ui/base/ime/android/cursor_anchor_info_controller.h ('k') | ui/base/ime/android/cursor_anchor_info_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698