blob: e33668249975797560256f98759074e1912308b8 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/vr/elements/content_element.h"
#include "chrome/browser/vr/model/text_input_info.h"
#include "chrome/browser/vr/ui_element_renderer.h"
#include "chrome/browser/vr/ui_scene_constants.h"
#include "chrome/browser/vr/vr_gl_util.h"
#include "third_party/blink/public/platform/web_gesture_event.h"
#include "ui/gfx/geometry/rect_f.h"
namespace vr {
namespace {
// If the screen space bounds or the aspect ratio of the content quad change
// beyond these thresholds we propagate the new content bounds so that the
// content's resolution can be adjusted.
static constexpr float kContentBoundsPropagationThreshold = 0.2f;
// Changes of the aspect ratio lead to a
// distorted content much more quickly. Thus, have a smaller threshold here.
static constexpr float kContentAspectRatioPropagationThreshold = 0.01f;
gfx::Vector3dF GetNormalFromTransform(const gfx::Transform& transform) {
gfx::Vector3dF x_axis(1, 0, 0);
gfx::Vector3dF y_axis(0, 1, 0);
transform.TransformVector(&x_axis);
transform.TransformVector(&y_axis);
gfx::Vector3dF normal = CrossProduct(x_axis, y_axis);
normal.GetNormalized(&normal);
return normal;
}
} // namespace
ContentElement::ContentElement(
ContentInputDelegate* delegate,
ContentElement::ScreenBoundsChangedCallback bounds_changed_callback)
: delegate_(delegate), bounds_changed_callback_(bounds_changed_callback) {
DCHECK(delegate);
set_scrollable(true);
}
ContentElement::~ContentElement() = default;
void ContentElement::Render(UiElementRenderer* renderer,
const CameraModel& model) const {
gfx::RectF copy_rect(0, 0, 1, 1);
if (texture_id_ || (overlay_texture_non_empty_ && overlay_texture_id_)) {
renderer->DrawTexturedQuad(
texture_id_, overlay_texture_non_empty_ ? overlay_texture_id_ : 0,
texture_location_, model.view_proj_matrix * world_space_transform(),
copy_rect, computed_opacity(), size(), corner_radius());
}
}
void ContentElement::OnFocusChanged(bool focused) {
if (delegate_)
delegate_->OnFocusChanged(focused);
focused_ = focused;
if (event_handlers_.focus_change)
event_handlers_.focus_change.Run(focused);
}
void ContentElement::OnInputEdited(const EditedText& info) {
delegate_->OnWebInputEdited(info, false);
}
void ContentElement::OnInputCommitted(const EditedText& info) {
delegate_->OnWebInputEdited(info, true);
}
void ContentElement::OnHoverEnter(const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentEnter(position);
}
void ContentElement::OnHoverLeave() {
if (delegate_)
delegate_->OnContentLeave();
}
void ContentElement::OnMove(const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentMove(position);
}
void ContentElement::OnButtonDown(const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentDown(position);
}
void ContentElement::OnButtonUp(const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentUp(position);
}
void ContentElement::OnFlingCancel(
std::unique_ptr<blink::WebGestureEvent> gesture,
const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentFlingCancel(std::move(gesture), position);
}
void ContentElement::OnScrollBegin(
std::unique_ptr<blink::WebGestureEvent> gesture,
const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentScrollBegin(std::move(gesture), position);
}
void ContentElement::OnScrollUpdate(
std::unique_ptr<blink::WebGestureEvent> gesture,
const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentScrollUpdate(std::move(gesture), position);
}
void ContentElement::OnScrollEnd(
std::unique_ptr<blink::WebGestureEvent> gesture,
const gfx::PointF& position) {
if (delegate_)
delegate_->OnContentScrollEnd(std::move(gesture), position);
}
void ContentElement::SetTextureId(unsigned int texture_id) {
texture_id_ = texture_id;
}
void ContentElement::SetTextureLocation(
UiElementRenderer::TextureLocation location) {
texture_location_ = location;
}
void ContentElement::SetOverlayTextureId(unsigned int texture_id) {
overlay_texture_id_ = texture_id;
}
void ContentElement::SetOverlayTextureLocation(
UiElementRenderer::TextureLocation location) {
overlay_texture_location_ = location;
}
void ContentElement::SetOverlayTextureEmpty(bool empty) {
overlay_texture_non_empty_ = !empty;
}
void ContentElement::SetProjectionMatrix(const gfx::Transform& matrix) {
projection_matrix_ = matrix;
}
void ContentElement::SetTextInputDelegate(
TextInputDelegate* text_input_delegate) {
text_input_delegate_ = text_input_delegate;
}
void ContentElement::RequestFocus() {
if (!text_input_delegate_)
return;
text_input_delegate_->RequestFocus(id());
}
void ContentElement::RequestUnfocus() {
if (!text_input_delegate_)
return;
text_input_delegate_->RequestUnfocus(id());
}
void ContentElement::UpdateInput(const EditedText& info) {
if (text_input_delegate_ && focused_)
text_input_delegate_->UpdateInput(info.current);
}
bool ContentElement::OnBeginFrame(const base::TimeTicks& time,
const gfx::Transform& head_pose) {
// TODO(mthiesse): This projection matrix is always going to be a frame
// behind when computing the content size. We'll need to address this somehow
// when we allow content resizing, or we could end up triggering an extra
// incorrect resize.
if (projection_matrix_.IsIdentity())
return false;
// Determine if the projected size of the content quad changed more than a
// given threshold. If so, propagate this info so that the content's
// resolution and size can be adjusted. For the calculation, we cannot take
// the content quad's actual size (main_content_->size()) if this property
// is animated. If we took the actual size during an animation we would
// surpass the threshold with differing projected sizes and aspect ratios
// (depending on the animation's timing). The differing values may cause
// visual artefacts if, for instance, the fullscreen aspect ratio is not 16:9.
// As a workaround, take the final size of the content quad after the
// animation as the basis for the calculation.
gfx::SizeF target_size = GetTargetSize();
// We take the target transform in case the content quad's parent's translate
// is animated. This approach only works with the current scene hierarchy and
// set of animated properties.
gfx::Transform target_transform = ComputeTargetWorldSpaceTransform();
gfx::Point3F target_center;
target_transform.TransformPoint(&target_center);
gfx::Vector3dF target_normal = GetNormalFromTransform(target_transform);
float distance = gfx::DotProduct(target_center - kOrigin, -target_normal);
gfx::SizeF screen_size =
CalculateScreenSize(projection_matrix_, distance, target_size);
float aspect_ratio = target_size.width() / target_size.height();
gfx::SizeF screen_bounds;
if (screen_size.width() < screen_size.height() * aspect_ratio) {
screen_bounds.set_width(screen_size.height() * aspect_ratio);
screen_bounds.set_height(screen_size.height());
} else {
screen_bounds.set_width(screen_size.width());
screen_bounds.set_height(screen_size.width() / aspect_ratio);
}
if (std::abs(screen_bounds.width() - last_content_screen_bounds_.width()) >
kContentBoundsPropagationThreshold ||
std::abs(screen_bounds.height() - last_content_screen_bounds_.height()) >
kContentBoundsPropagationThreshold ||
std::abs(aspect_ratio - last_content_aspect_ratio_) >
kContentAspectRatioPropagationThreshold) {
bounds_changed_callback_.Run(screen_bounds);
last_content_screen_bounds_.set_width(screen_bounds.width());
last_content_screen_bounds_.set_height(screen_bounds.height());
last_content_aspect_ratio_ = aspect_ratio;
return true;
}
return false;
}
void ContentElement::SetDelegate(ContentInputDelegate* delegate) {
delegate_ = delegate;
}
} // namespace vr