blob: 08be4e0b72e089859404288131d7d32d71cfe9ee [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/touch_selection/touch_handle_drawable_aura.h"
#include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/hit_test.h"
#include "ui/base/models/image_model.h"
#include "ui/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/color/color_provider_manager.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/outsets_f.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/image/image.h"
#include "ui/native_theme/native_theme.h"
#include "ui/native_theme/native_theme_observer.h"
#include "ui/touch_selection//vector_icons/vector_icons.h"
namespace ui {
namespace {
// Padding to apply horizontally around and vertically below the handle image,
// so that touch events near the handle image are targeted to the handle.
constexpr int kSelectionHandlePadding = 6;
// Max opacity of the selection handle image.
constexpr float kSelectionHandleMaxOpacity = 0.8f;
// Epsilon value used to compare float values to zero.
constexpr float kEpsilon = 1e-8f;
// Returns the appropriate handle vector icon based on the handle orientation.
ImageModel GetHandleVectorIcon(TouchHandleOrientation orientation) {
const gfx::VectorIcon* icon = nullptr;
switch (orientation) {
case TouchHandleOrientation::LEFT:
icon = &kTextSelectionHandleLeftIcon;
break;
case TouchHandleOrientation::CENTER:
icon = &kTextSelectionHandleCenterIcon;
break;
case TouchHandleOrientation::RIGHT:
icon = &kTextSelectionHandleRightIcon;
break;
case TouchHandleOrientation::UNDEFINED:
NOTREACHED() << "Invalid touch handle bound type.";
}
return ImageModel::FromVectorIcon(*icon,
/*color_id=*/kColorSysPrimary);
}
bool IsNearlyZero(float value) {
return std::abs(value) < kEpsilon;
}
} // namespace
TouchHandleDrawableAura::TouchHandleDrawableAura(aura::Window* parent)
: window_(std::make_unique<aura::Window>(/*delegate=*/nullptr)),
enabled_(false),
alpha_(0),
orientation_(TouchHandleOrientation::UNDEFINED) {
window_->Init(LAYER_TEXTURED);
window_->SetTransparent(true);
window_->set_owned_by_parent(false);
window_->SetEventTargetingPolicy(aura::EventTargetingPolicy::kNone);
window_->layer()->set_delegate(this);
parent->AddChild(window_.get());
theme_observation_.Observe(NativeTheme::GetInstanceForNativeUi());
}
TouchHandleDrawableAura::~TouchHandleDrawableAura() = default;
void TouchHandleDrawableAura::UpdateWindowBounds() {
gfx::Rect window_bounds(gfx::ToRoundedPoint(targetable_origin_),
handle_image_.Size());
// Offset the window bounds to account for space between the origin of the
// targetable area and the handle image.
window_bounds.Offset(kSelectionHandlePadding, 0);
window_->SetBounds(window_bounds);
}
bool TouchHandleDrawableAura::IsVisible() const {
return enabled_ && !IsNearlyZero(alpha_);
}
void TouchHandleDrawableAura::SetEnabled(bool enabled) {
if (enabled == enabled_)
return;
enabled_ = enabled;
if (IsVisible())
window_->Show();
else
window_->Hide();
}
void TouchHandleDrawableAura::SetOrientation(TouchHandleOrientation orientation,
bool mirror_vertical,
bool mirror_horizontal) {
// TODO(AviD): Implement adaptive handle orientation logic for Aura
DCHECK(!mirror_vertical);
DCHECK(!mirror_horizontal);
if (orientation_ == orientation)
return;
orientation_ = orientation;
handle_image_ = GetHandleVectorIcon(orientation);
UpdateWindowBounds();
window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
}
void TouchHandleDrawableAura::SetOrigin(const gfx::PointF& position) {
targetable_origin_ = position;
UpdateWindowBounds();
}
void TouchHandleDrawableAura::SetAlpha(float alpha) {
if (alpha == alpha_)
return;
alpha_ = alpha;
window_->layer()->SetOpacity(alpha_ * kSelectionHandleMaxOpacity);
if (IsVisible())
window_->Show();
else
window_->Hide();
}
gfx::RectF TouchHandleDrawableAura::GetVisibleBounds() const {
// These bounds are used to determine the area that can be used for targeting
// the handle, so we include the transparent padding added around the handle
// image even though it technically isn't visible.
gfx::RectF targetable_bounds(window_->bounds());
targetable_bounds.Outset(gfx::OutsetsF::TLBR(0, kSelectionHandlePadding,
kSelectionHandlePadding,
kSelectionHandlePadding));
return targetable_bounds;
}
float TouchHandleDrawableAura::GetDrawableHorizontalPaddingRatio() const {
// The ratio returned by this function is used to position the touch handle
// targetable area relative to the focal point (e.g. bottom of text caret).
// So, even though padding is applied on both the left and right of the handle
// image, we compute the ratio based on the padding on only one side.
return kSelectionHandlePadding /
(window_->bounds().width() + 2.0f * kSelectionHandlePadding);
}
void TouchHandleDrawableAura::OnPaintLayer(const PaintContext& context) {
PaintRecorder recorder(context, window_->bounds().size());
if (!handle_image_.IsEmpty()) {
recorder.canvas()->DrawImageInt(
handle_image_.Rasterize(ColorProviderManager::Get().GetColorProviderFor(
NativeTheme::GetInstanceForNativeUi()->GetColorProviderKey(
nullptr))),
0, 0);
}
}
void TouchHandleDrawableAura::OnNativeThemeUpdated(
NativeTheme* observed_theme) {
window_->SchedulePaintInRect(gfx::Rect(window_->bounds().size()));
}
} // namespace ui