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