| Index: ui/base/ime/android/cursor_anchor_info_controller.cc
|
| diff --git a/ui/base/ime/android/cursor_anchor_info_controller.cc b/ui/base/ime/android/cursor_anchor_info_controller.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e2405b0990c18bd9f860ccbc34200d1528cd7c28
|
| --- /dev/null
|
| +++ b/ui/base/ime/android/cursor_anchor_info_controller.cc
|
| @@ -0,0 +1,329 @@
|
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "ui/base/ime/android/cursor_anchor_info_controller.h"
|
| +
|
| +#include "base/android/build_info.h"
|
| +#include "base/strings/string_piece.h"
|
| +#include "base/strings/stringprintf.h"
|
| +#include "cc/output/compositor_frame_metadata.h"
|
| +#include "cc/output/viewport_selection_bound.h"
|
| +#include "ui/base/ime/android/cursor_anchor_info_builder.h"
|
| +#include "ui/base/ime/android/cursor_anchor_info_sender.h"
|
| +#include "ui/base/ui_base_switches_util.h"
|
| +
|
| +namespace ui {
|
| +namespace {
|
| +
|
| +class CursorAnchorInfoControllerImpl final
|
| + : public CursorAnchorInfoController {
|
| + public:
|
| + CursorAnchorInfoControllerImpl(
|
| + CursorAnchorInfoSender* sender,
|
| + scoped_ptr<CursorAnchorInfoBuilder> builder);
|
| +
|
| + ~CursorAnchorInfoControllerImpl() override;
|
| +
|
| + void OnFocusedNodeChanged(bool is_editable_node) override;
|
| +
|
| + void OnTextInputStateChanged(
|
| + const base::string16& text,
|
| + const gfx::Range& selection_range,
|
| + const gfx::Range& composition_range) override;
|
| +
|
| + void OnCompositionRangeChanged(
|
| + const gfx::Range& range,
|
| + const std::vector<gfx::Rect>& character_bounds) override;
|
| +
|
| + void OnFrameMetadataUpdated(
|
| + const cc::CompositorFrameMetadata& frame_metadata,
|
| + const gfx::Vector2d& view_offset) override;
|
| +
|
| + bool OnRequestCursorUpdates(uint32 cursor_update_mode) override;
|
| +
|
| + private:
|
| + void UpdateBuilder();
|
| + void ScheduleUpdate();
|
| + bool HasPendingUpdate() const;
|
| + void ClearPendingUpdate();
|
| + void SetMonitorMode(bool enabled);
|
| +
|
| + // Doesn't own.
|
| + CursorAnchorInfoSender* cursor_anchor_info_sender_;
|
| + scoped_ptr<CursorAnchorInfoBuilder> cursor_anchor_info_builder_;
|
| +
|
| + bool editable_;
|
| +
|
| + base::string16 text_;
|
| + gfx::Range selection_range_;
|
| + gfx::Range composition_range_;
|
| + std::vector<gfx::Rect> character_bounds_;
|
| + gfx::Range character_bounds_range_;
|
| + bool has_insertion_marker_;
|
| + bool is_insertion_marker_visible_;
|
| + gfx::RectF insertion_marker_rect_;
|
| + bool has_coordinate_info_;
|
| + float scale_;
|
| + gfx::Vector2dF translation_;
|
| +
|
| + bool has_pending_request_;
|
| + bool monitor_mode_enabled_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(CursorAnchorInfoControllerImpl);
|
| +};
|
| +
|
| +CursorAnchorInfoControllerImpl::CursorAnchorInfoControllerImpl(
|
| + CursorAnchorInfoSender* sender,
|
| + scoped_ptr<CursorAnchorInfoBuilder> builder)
|
| + : cursor_anchor_info_sender_(sender),
|
| + cursor_anchor_info_builder_(builder.Pass()),
|
| + editable_(false),
|
| + selection_range_(gfx::Range::InvalidRange()),
|
| + composition_range_(gfx::Range::InvalidRange()),
|
| + has_insertion_marker_(false),
|
| + is_insertion_marker_visible_(false),
|
| + has_coordinate_info_(false),
|
| + scale_(1.0f),
|
| + has_pending_request_(false),
|
| + monitor_mode_enabled_(false) {
|
| +}
|
| +
|
| +CursorAnchorInfoControllerImpl::~CursorAnchorInfoControllerImpl() {
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::OnFocusedNodeChanged(
|
| + bool is_editable_node) {
|
| + editable_ = is_editable_node;
|
| + text_.clear();
|
| + selection_range_ = gfx::Range::InvalidRange();
|
| + composition_range_ = gfx::Range::InvalidRange();
|
| + character_bounds_.clear();
|
| + character_bounds_range_ = gfx::Range::InvalidRange();
|
| + has_insertion_marker_ = false;
|
| + is_insertion_marker_visible_= false;
|
| + insertion_marker_rect_.SetRect(0.0f, 0.0f, 0.0f, 0.0f);
|
| + has_coordinate_info_ = false;
|
| + scale_ = 1.0f;
|
| + translation_ = gfx::Vector2d();
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::OnTextInputStateChanged(
|
| + const base::string16& text,
|
| + const gfx::Range& selection_range,
|
| + const gfx::Range& composition_range) {
|
| + if (!editable_)
|
| + return;
|
| +
|
| + if (monitor_mode_enabled_) {
|
| + const bool changed =
|
| + text_ != text
|
| + || selection_range_ != selection_range
|
| + || composition_range_ != composition_range;
|
| + if (changed)
|
| + has_pending_request_ = true;
|
| + }
|
| +
|
| + text_ = text;
|
| + selection_range_ = selection_range;
|
| + composition_range_ = composition_range;
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::OnCompositionRangeChanged(
|
| + const gfx::Range& range,
|
| + const std::vector<gfx::Rect>& character_bounds) {
|
| + if (!editable_)
|
| + return;
|
| +
|
| + if (monitor_mode_enabled_) {
|
| + const bool changed =
|
| + character_bounds_ != character_bounds
|
| + || character_bounds_range_ != range;
|
| + if (changed)
|
| + has_pending_request_ = true;
|
| + }
|
| +
|
| + character_bounds_ = character_bounds;
|
| + character_bounds_range_ = range;
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::OnFrameMetadataUpdated(
|
| + const cc::CompositorFrameMetadata& frame_metadata,
|
| + const gfx::Vector2d& view_offset) {
|
| + if (!editable_)
|
| + return;
|
| +
|
| + const auto& selection_start = frame_metadata.selection_start;
|
| +
|
| + const float scale =
|
| + frame_metadata.device_scale_factor * frame_metadata.page_scale_factor;
|
| + const auto translation =
|
| + gfx::ScaleVector2d(frame_metadata.root_scroll_offset, -scale)
|
| + + gfx::Vector2dF(view_offset.x(), view_offset.y())
|
| + + frame_metadata.location_bar_offset
|
| + + frame_metadata.location_bar_content_translation;
|
| +
|
| + const bool has_insertion_marker =
|
| + selection_start.type == cc::SELECTION_BOUND_CENTER;
|
| + const bool is_insertion_marker_visible = selection_start.visible;
|
| + const gfx::PointF insertion_marker_origin = has_insertion_marker
|
| + ? selection_start.edge_top
|
| + : gfx::PointF(0.0f, 0.0f);
|
| + const float insertion_marker_height = has_insertion_marker
|
| + ? selection_start.edge_bottom.y() - selection_start.edge_top.y()
|
| + : 0.0f;
|
| + const gfx::SizeF insertion_marker_size(0.0f, insertion_marker_height);
|
| + const gfx::RectF insertion_marker_rect(insertion_marker_origin,
|
| + insertion_marker_size);
|
| + if (monitor_mode_enabled_) {
|
| + const bool changed =
|
| + !has_coordinate_info_
|
| + || scale_ != scale
|
| + || translation_ != translation
|
| + || has_insertion_marker_ != has_insertion_marker
|
| + || is_insertion_marker_visible_ != is_insertion_marker_visible
|
| + || insertion_marker_rect_ != insertion_marker_rect;
|
| + if (changed)
|
| + has_pending_request_ = true;
|
| + }
|
| +
|
| + scale_ = scale;
|
| + translation_ = translation;
|
| + has_insertion_marker_ = has_insertion_marker;
|
| + is_insertion_marker_visible_ = is_insertion_marker_visible;
|
| + insertion_marker_rect_ = insertion_marker_rect;
|
| + has_coordinate_info_ = true;
|
| +
|
| + if (cursor_anchor_info_sender_
|
| + && cursor_anchor_info_builder_
|
| + && HasPendingUpdate()) {
|
| + UpdateBuilder();
|
| + cursor_anchor_info_sender_->SendCursorAnchorInfo(
|
| + cursor_anchor_info_builder_.get());
|
| + ClearPendingUpdate();
|
| + }
|
| +}
|
| +
|
| +bool CursorAnchorInfoControllerImpl::OnRequestCursorUpdates(
|
| + uint32 cursor_update_mode) {
|
| + if (!editable_)
|
| + return false;
|
| +
|
| + // Should return false if at least one unknown bit is set, because we don't
|
| + // know what is requested by such a bit.
|
| + const static uint32 kKnownFlags =
|
| + kCursorUpdateModeImmediate | kCursorUpdateModeMonitor;
|
| + if ((cursor_update_mode & ~kKnownFlags) != 0)
|
| + return false;
|
| +
|
| + const bool update_immediate = cursor_update_mode & kCursorUpdateModeImmediate;
|
| + const bool update_monitor = cursor_update_mode & kCursorUpdateModeMonitor;
|
| +
|
| + SetMonitorMode(update_monitor);
|
| + if (update_immediate)
|
| + ScheduleUpdate();
|
| +
|
| + if (cursor_anchor_info_sender_
|
| + && cursor_anchor_info_builder_
|
| + && HasPendingUpdate()) {
|
| + UpdateBuilder();
|
| + cursor_anchor_info_sender_->SendCursorAnchorInfo(
|
| + cursor_anchor_info_builder_.get());
|
| + ClearPendingUpdate();
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::ScheduleUpdate() {
|
| + has_pending_request_ = true;
|
| +}
|
| +
|
| +bool CursorAnchorInfoControllerImpl::HasPendingUpdate() const {
|
| + return has_pending_request_ && has_coordinate_info_;
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::ClearPendingUpdate() {
|
| + has_pending_request_ = false;
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::SetMonitorMode(bool enabled) {
|
| + monitor_mode_enabled_ = enabled;
|
| +}
|
| +
|
| +void CursorAnchorInfoControllerImpl::UpdateBuilder() {
|
| + DCHECK(HasPendingUpdate());
|
| +
|
| + cursor_anchor_info_builder_->Reset();
|
| +
|
| + if (selection_range_.IsValid())
|
| + cursor_anchor_info_builder_->SetSelectionRange(selection_range_);
|
| +
|
| + if (composition_range_.IsValid()) {
|
| + const auto composition_text = base::StringPiece16(text_).substr(
|
| + composition_range_.start(),
|
| + composition_range_.length());
|
| + cursor_anchor_info_builder_->SetComposingText(
|
| + composition_range_.start(),
|
| + composition_text);
|
| + }
|
| + if (character_bounds_range_.IsValid()) {
|
| + const int index_offset = character_bounds_range_.start();
|
| + for (size_t i = 0; i < character_bounds_.size(); ++i) {
|
| + cursor_anchor_info_builder_->AddCharacterBounds(
|
| + index_offset + i,
|
| + character_bounds_[i],
|
| + CursorAnchorInfoBuilder::kFlagHasVisibleRegion);
|
| + }
|
| + }
|
| +
|
| + if (has_insertion_marker_) {
|
| + const uint32 flags = is_insertion_marker_visible_
|
| + ? CursorAnchorInfoBuilder::kFlagHasVisibleRegion
|
| + : CursorAnchorInfoBuilder::kFlagHasInvisibleRegion;
|
| + cursor_anchor_info_builder_->SetInsertionMarkerLocation(
|
| + insertion_marker_rect_.x(),
|
| + insertion_marker_rect_.y(),
|
| + insertion_marker_rect_.bottom(),
|
| + insertion_marker_rect_.bottom(),
|
| + flags);
|
| + }
|
| + cursor_anchor_info_builder_->SetScaleAndTranslate(scale_,
|
| + translation_);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +CursorAnchorInfoController::CursorAnchorInfoController() {
|
| +}
|
| +
|
| +CursorAnchorInfoController::~CursorAnchorInfoController() {
|
| +}
|
| +
|
| +// static
|
| +scoped_ptr<CursorAnchorInfoController>
|
| +CursorAnchorInfoController::Create(CursorAnchorInfoSender* sender) {
|
| + if (!switches::IsCursorAnchorInfoEnabled())
|
| + return scoped_ptr<ui::CursorAnchorInfoController>();
|
| +
|
| + static bool use_cursor_anchor_info =
|
| + base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
|
| + if (!use_cursor_anchor_info)
|
| + return scoped_ptr<ui::CursorAnchorInfoController>();
|
| +
|
| + return scoped_ptr<ui::CursorAnchorInfoController>(
|
| + new ui::CursorAnchorInfoControllerImpl(
|
| + sender,
|
| + CursorAnchorInfoBuilder::Create()));
|
| +}
|
| +
|
| +// static
|
| +scoped_ptr<CursorAnchorInfoController>
|
| +CursorAnchorInfoController::CreateForTest(
|
| + CursorAnchorInfoSender* sender,
|
| + scoped_ptr<ui::CursorAnchorInfoBuilder> builder) {
|
| + return scoped_ptr<CursorAnchorInfoController>(
|
| + new ui::CursorAnchorInfoControllerImpl(sender, builder.Pass()));
|
| +}
|
| +
|
| +} // namespace ui
|
|
|