OLD | NEW |
---|---|
(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 | |
OLD | NEW |