blob: cf5e42925b6510d85818aa94e6315e6478f68a51 [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/ui_scene_creator.h"
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/i18n/case_conversion.h"
#include "base/numerics/math_constants.h"
#include "chrome/browser/vr/databinding/binding.h"
#include "chrome/browser/vr/databinding/vector_binding.h"
#include "chrome/browser/vr/elements/audio_permission_prompt.h"
#include "chrome/browser/vr/elements/button.h"
#include "chrome/browser/vr/elements/content_element.h"
#include "chrome/browser/vr/elements/controller.h"
#include "chrome/browser/vr/elements/disc_button.h"
#include "chrome/browser/vr/elements/draw_phase.h"
#include "chrome/browser/vr/elements/environment/background.h"
#include "chrome/browser/vr/elements/environment/grid.h"
#include "chrome/browser/vr/elements/environment/stars.h"
#include "chrome/browser/vr/elements/exit_prompt.h"
#include "chrome/browser/vr/elements/full_screen_rect.h"
#include "chrome/browser/vr/elements/invisible_hit_target.h"
#include "chrome/browser/vr/elements/keyboard.h"
#include "chrome/browser/vr/elements/laser.h"
#include "chrome/browser/vr/elements/linear_layout.h"
#include "chrome/browser/vr/elements/omnibox_formatting.h"
#include "chrome/browser/vr/elements/rect.h"
#include "chrome/browser/vr/elements/repositioner.h"
#include "chrome/browser/vr/elements/reticle.h"
#include "chrome/browser/vr/elements/scaled_depth_adjuster.h"
#include "chrome/browser/vr/elements/spinner.h"
#include "chrome/browser/vr/elements/text.h"
#include "chrome/browser/vr/elements/text_input.h"
#include "chrome/browser/vr/elements/throbber.h"
#include "chrome/browser/vr/elements/toast.h"
#include "chrome/browser/vr/elements/transient_element.h"
#include "chrome/browser/vr/elements/ui_element.h"
#include "chrome/browser/vr/elements/ui_element_name.h"
#include "chrome/browser/vr/elements/ui_texture.h"
#include "chrome/browser/vr/elements/url_bar.h"
#include "chrome/browser/vr/elements/vector_icon.h"
#include "chrome/browser/vr/elements/viewport_aware_root.h"
#include "chrome/browser/vr/elements/webvr_url_toast.h"
#include "chrome/browser/vr/keyboard_delegate.h"
#include "chrome/browser/vr/model/model.h"
#include "chrome/browser/vr/speech_recognizer.h"
#include "chrome/browser/vr/target_property.h"
#include "chrome/browser/vr/ui.h"
#include "chrome/browser/vr/ui_browser_interface.h"
#include "chrome/browser/vr/ui_scene.h"
#include "chrome/browser/vr/ui_scene_constants.h"
#include "chrome/browser/vr/vector_icons/vector_icons.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/transform_util.h"
namespace vr {
namespace {
template <typename V, typename C, typename S>
void BindColor(Model* model,
V* view,
C color,
const std::string& color_string,
S setter,
const std::string& setter_string) {
view->AddBinding(std::make_unique<Binding<SkColor>>(
base::BindRepeating([](Model* m, C c) { return (m->color_scheme()).*c; },
base::Unretained(model), color),
color_string,
base::BindRepeating(
[](V* v, S s, const SkColor& value) { (v->*s)(value); },
base::Unretained(view), setter),
setter_string));
}
#ifndef NDEBUG
#define VR_BIND_COLOR(m, v, c, s) BindColor(m, v, c, #c, s, #s)
#else
#define VR_BIND_COLOR(m, v, c, s) BindColor(m, v, c, "", s, "")
#endif
template <typename V, typename C, typename S>
void BindButtonColors(Model* model,
V* view,
C colors,
const std::string& colors_string,
S setter,
const std::string& setter_string) {
view->AddBinding(std::make_unique<Binding<ButtonColors>>(
base::BindRepeating([](Model* m, C c) { return (m->color_scheme()).*c; },
base::Unretained(model), colors),
colors_string,
base::BindRepeating(
[](V* v, S s, const ButtonColors& value) { (v->*s)(value); },
base::Unretained(view), setter),
setter_string));
}
#ifndef NDEBUG
#define VR_BIND_BUTTON_COLORS(m, v, c, s) BindButtonColors(m, v, c, #c, s, #s)
#else
#define VR_BIND_BUTTON_COLORS(m, v, c, s) BindButtonColors(m, v, c, "", s, "")
#endif
#define VR_BIND_VISIBILITY(v, c) \
v->AddBinding( \
VR_BIND_FUNC(bool, Model, model_, c, UiElement, v.get(), SetVisible));
template <typename T, typename... Args>
std::unique_ptr<T> Create(UiElementName name, DrawPhase phase, Args&&... args) {
auto element = std::make_unique<T>(std::forward<Args>(args)...);
element->SetName(name);
element->SetDrawPhase(phase);
return element;
}
typedef VectorBinding<OmniboxSuggestion, Button> SuggestionSetBinding;
typedef typename SuggestionSetBinding::ElementBinding SuggestionBinding;
void OnSuggestionModelAdded(UiScene* scene,
UiBrowserInterface* browser,
Ui* ui,
Model* model,
SuggestionBinding* element_binding) {
auto icon = std::make_unique<VectorIcon>(100);
icon->SetDrawPhase(kPhaseForeground);
icon->SetType(kTypeOmniboxSuggestionIcon);
icon->set_hit_testable(false);
icon->SetSize(kSuggestionIconSizeDMM, kSuggestionIconSizeDMM);
VR_BIND_COLOR(model, icon.get(), &ColorScheme::omnibox_icon,
&VectorIcon::SetColor);
VectorIcon* p_icon = icon.get();
auto icon_box = std::make_unique<UiElement>();
icon_box->SetDrawPhase(kPhaseNone);
icon_box->SetType(kTypeOmniboxSuggestionIconField);
icon_box->SetSize(kSuggestionIconFieldWidthDMM, kSuggestionHeightDMM);
icon_box->AddChild(std::move(icon));
auto content_text = std::make_unique<Text>(kSuggestionContentTextHeightDMM);
content_text->SetDrawPhase(kPhaseForeground);
content_text->SetType(kTypeOmniboxSuggestionContentText);
content_text->set_hit_testable(false);
content_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
content_text->SetSize(kSuggestionTextFieldWidthDMM, 0);
content_text->SetAlignment(UiTexture::kTextAlignmentLeft);
Text* p_content_text = content_text.get();
auto description_text =
std::make_unique<Text>(kSuggestionDescriptionTextHeightDMM);
description_text->SetDrawPhase(kPhaseForeground);
description_text->SetType(kTypeOmniboxSuggestionDescriptionText);
description_text->set_hit_testable(false);
description_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
description_text->SetSize(kSuggestionTextFieldWidthDMM, 0);
description_text->SetAlignment(UiTexture::kTextAlignmentLeft);
Text* p_description_text = description_text.get();
auto text_layout = std::make_unique<LinearLayout>(LinearLayout::kDown);
text_layout->SetType(kTypeOmniboxSuggestionTextLayout);
text_layout->set_hit_testable(false);
text_layout->set_margin(kSuggestionLineGapDMM);
text_layout->AddChild(std::move(content_text));
text_layout->AddChild(std::move(description_text));
auto right_margin = std::make_unique<UiElement>();
right_margin->SetDrawPhase(kPhaseNone);
right_margin->SetSize(kSuggestionRightMarginDMM, kSuggestionHeightDMM);
auto suggestion_layout = std::make_unique<LinearLayout>(LinearLayout::kRight);
suggestion_layout->SetType(kTypeOmniboxSuggestionLayout);
suggestion_layout->set_hit_testable(false);
suggestion_layout->AddChild(std::move(icon_box));
suggestion_layout->AddChild(std::move(text_layout));
suggestion_layout->AddChild(std::move(right_margin));
auto background = Create<Button>(
kNone, kPhaseForeground,
base::BindRepeating(
[](UiBrowserInterface* b, Ui* ui, Model* m, SuggestionBinding* e) {
b->Navigate(e->model()->destination);
ui->OnUiRequestedNavigation();
},
base::Unretained(browser), base::Unretained(ui),
base::Unretained(model), base::Unretained(element_binding)));
background->SetType(kTypeOmniboxSuggestionBackground);
background->set_hit_testable(true);
background->set_bubble_events(true);
background->set_bounds_contain_children(true);
background->set_hover_offset(0.0);
VR_BIND_BUTTON_COLORS(model, background.get(),
&ColorScheme::suggestion_button_colors,
&Button::SetButtonColors);
background->AddChild(std::move(suggestion_layout));
element_binding->bindings().push_back(
VR_BIND_FUNC(base::string16, SuggestionBinding, element_binding,
model->model()->contents, Text, p_content_text, SetText));
element_binding->bindings().push_back(
std::make_unique<Binding<TextFormatting>>(
VR_BIND_LAMBDA(
[](SuggestionBinding* suggestion, Model* model) {
return ConvertClassification(
suggestion->model()->contents_classifications,
suggestion->model()->contents.size(),
model->color_scheme());
},
base::Unretained(element_binding), base::Unretained(model)),
VR_BIND_LAMBDA(
[](Text* v, const TextFormatting& formatting) {
v->SetFormatting(formatting);
},
base::Unretained(p_content_text))));
element_binding->bindings().push_back(
std::make_unique<Binding<base::string16>>(
VR_BIND_LAMBDA(
[](SuggestionBinding* m) { return m->model()->description; },
base::Unretained(element_binding)),
VR_BIND_LAMBDA(
[](Text* v, const base::string16& text) {
v->SetVisibleImmediately(!text.empty());
v->set_requires_layout(!text.empty());
if (!text.empty()) {
v->SetText(text);
}
},
base::Unretained(p_description_text))));
element_binding->bindings().push_back(
std::make_unique<Binding<TextFormatting>>(
VR_BIND_LAMBDA(
[](SuggestionBinding* suggestion, Model* model) {
return ConvertClassification(
suggestion->model()->description_classifications,
suggestion->model()->description.size(),
model->color_scheme());
},
base::Unretained(element_binding), base::Unretained(model)),
VR_BIND_LAMBDA(
[](Text* v, const TextFormatting& formatting) {
v->SetFormatting(formatting);
},
base::Unretained(p_description_text))));
element_binding->bindings().push_back(
VR_BIND(AutocompleteMatch::Type, SuggestionBinding, element_binding,
model->model()->type, VectorIcon, p_icon,
view->SetIcon(AutocompleteMatch::TypeToVectorIcon(value))));
element_binding->set_view(background.get());
scene->AddUiElement(kOmniboxSuggestions, std::move(background));
}
void OnSuggestionModelRemoved(UiScene* scene, SuggestionBinding* binding) {
scene->RemoveUiElement(binding->view()->id());
}
std::unique_ptr<TransientElement> CreateTransientParent(UiElementName name,
int timeout_seconds,
bool animate_opacity) {
auto element = std::make_unique<SimpleTransientElement>(
base::TimeDelta::FromSeconds(timeout_seconds));
element->SetName(name);
element->SetVisible(false);
element->set_hit_testable(false);
if (animate_opacity)
element->SetTransitionedProperties({OPACITY});
return element;
}
std::unique_ptr<Rect> CreateOmniboxSpacer(Model* model) {
auto spacer = Create<Rect>(kNone, kPhaseForeground);
spacer->SetType(kTypeOmniboxSuggestionSpacer);
spacer->SetSize(kOmniboxWidthDMM, kSuggestionVerticalPaddingDMM);
spacer->set_focusable(false);
spacer->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return !m->omnibox_suggestions.empty(); },
base::Unretained(model)),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& v) {
e->SetVisible(v);
e->set_requires_layout(v);
},
base::Unretained(spacer.get()))));
VR_BIND_COLOR(model, spacer.get(), &ColorScheme::omnibox_background,
&Rect::SetColor);
return spacer;
}
// Util to bind the visibility of the given control element to the given
// property in the model and the visibility of the voice search UI root.
#define BIND_VISIBILITY_CONTROL_FOR_VOICE(control_element, model, property) \
control_element->AddBinding(std::make_unique<Binding<bool>>( \
VR_BIND_LAMBDA( \
[](Model* model, UiElement* voice_search_root) { \
return model->property && \
voice_search_root->GetTargetOpacity() == 0.f; \
}, \
base::Unretained(model), \
base::Unretained( \
scene_->GetUiElementByName(kSpeechRecognitionRoot))), \
VR_BIND_LAMBDA([](UiElement* control, \
const bool& value) { control->SetVisible(value); }, \
base::Unretained(control_element))))
std::unique_ptr<UiElement> CreateSpacer(float width, float height) {
auto spacer = Create<UiElement>(kNone, kPhaseNone);
spacer->SetSize(width, height);
spacer->set_hit_testable(false);
return spacer;
}
std::unique_ptr<UiElement> CreateSnackbar(
UiElementName name,
Model* model,
const gfx::VectorIcon& vector_icon,
const base::string16& label,
const base::string16& button_label,
base::RepeatingCallback<void()> callback) {
auto scaler = std::make_unique<ScaledDepthAdjuster>(kSnackbarDistance);
scaler->set_hit_testable(false);
auto snackbar_layout =
Create<LinearLayout>(name, kPhaseNone, LinearLayout::kRight);
snackbar_layout->SetTranslate(0, kSnackbarDistance * sin(kSnackbarAngle), 0);
snackbar_layout->SetRotate(1, 0, 0, kSnackbarAngle);
snackbar_layout->set_hit_testable(false);
float radius = 0.5f * kSnackbarHeightDMM;
auto snackbar_oval_left = Create<Rect>(kNone, kPhaseForeground);
snackbar_oval_left->SetType(kTypeSnackbarDescription);
snackbar_oval_left->SetSize(0, kSnackbarHeightDMM);
snackbar_oval_left->SetCornerRadii({radius, 0, radius, 0});
snackbar_oval_left->set_owner_name_for_test(name);
VR_BIND_COLOR(model, snackbar_oval_left.get(),
&ColorScheme::snackbar_background, &Rect::SetColor);
auto snackbar_inner_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
snackbar_inner_layout->set_margin(kSnackbarPaddingDMM * 0.5f);
snackbar_inner_layout->set_hit_testable(false);
snackbar_oval_left->AddBinding(VR_BIND(
float, UiElement, snackbar_inner_layout.get(),
model->stale_size().width() + kSnackbarPaddingDMM, UiElement,
snackbar_oval_left.get(), view->SetSize(value, kSnackbarHeightDMM)));
auto icon = Create<VectorIcon>(kNone, kPhaseForeground, 256);
icon->SetSize(kSnackbarIconWidthDMM, kSnackbarIconWidthDMM);
icon->SetBackgroundColor(SK_ColorTRANSPARENT);
icon->SetIcon(vector_icon);
icon->set_hit_testable(false);
VR_BIND_COLOR(model, icon.get(), &ColorScheme::snackbar_foreground,
&VectorIcon::SetColor);
auto text = Create<Text>(kNone, kPhaseForeground, kSnackbarFontHeightDMM);
text->SetText(label);
text->SetLayoutMode(TextLayoutMode::kSingleLineFixedHeight);
text->set_hit_testable(false);
VR_BIND_COLOR(model, text.get(), &ColorScheme::snackbar_foreground,
&Text::SetColor);
auto button = Create<Button>(kNone, kPhaseForeground, callback);
button->SetType(kTypeSnackbarButton);
VR_BIND_BUTTON_COLORS(model, button.get(),
&ColorScheme::snackbar_button_colors,
&Button::SetButtonColors);
button->set_hover_offset(0.0f);
button->SetCornerRadii({0, radius, 0, radius});
button->SetSize(0, kSnackbarHeightDMM);
button->set_owner_name_for_test(name);
button->set_hit_testable(true);
auto button_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
button_layout->set_hit_testable(false);
auto button_text =
Create<Text>(kNone, kPhaseForeground, kSnackbarFontHeightDMM);
button_text->SetText(button_label);
button_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedHeight);
button_text->set_hit_testable(false);
button_text->AddBinding(
VR_BIND_FUNC(SkColor, Model, model,
model->color_scheme().snackbar_button_colors.foreground,
Text, button_text.get(), SetColor));
button->AddBinding(VR_BIND(float, UiElement, button_layout.get(),
model->stale_size().width() + kSnackbarPaddingDMM,
UiElement, button.get(),
view->SetSize(value, kSnackbarHeightDMM)));
button_layout->AddChild(std::move(button_text));
button_layout->AddChild(CreateSpacer(0.5f * kSnackbarPaddingDMM, 0.0f));
button->AddChild(std::move(button_layout));
snackbar_inner_layout->AddChild(CreateSpacer(0.0f, 0.0f));
snackbar_inner_layout->AddChild(std::move(icon));
snackbar_inner_layout->AddChild(std::move(text));
snackbar_oval_left->AddChild(std::move(snackbar_inner_layout));
snackbar_layout->AddChild(std::move(snackbar_oval_left));
snackbar_layout->AddChild(std::move(button));
scaler->AddChild(std::move(snackbar_layout));
auto snackbar_root = Create<UiElement>(kNone, kPhaseNone);
snackbar_root->set_hit_testable(false);
snackbar_root->AddChild(std::move(scaler));
return snackbar_root;
}
} // namespace
UiSceneCreator::UiSceneCreator(UiBrowserInterface* browser,
UiScene* scene,
Ui* ui,
ContentInputDelegate* content_input_delegate,
KeyboardDelegate* keyboard_delegate,
TextInputDelegate* text_input_delegate,
Model* model)
: browser_(browser),
scene_(scene),
ui_(ui),
content_input_delegate_(content_input_delegate),
keyboard_delegate_(keyboard_delegate),
text_input_delegate_(text_input_delegate),
model_(model) {}
UiSceneCreator::~UiSceneCreator() {}
void UiSceneCreator::CreateScene() {
Create2dBrowsingSubtreeRoots();
CreateWebVrRoot();
CreateBackground();
CreateViewportAwareRoot();
CreateContentQuad();
CreateExitPrompt();
CreateAudioPermissionPrompt();
CreateSystemIndicators();
CreateUrlBar();
CreateLoadingIndicator();
if (model_->update_ready_snackbar_enabled) {
CreateSnackbars();
}
CreateOmnibox();
CreateCloseButton();
CreateFullscreenToast();
CreateUnderDevelopmentNotice();
CreateVoiceSearchUiGroup();
CreateExitWarning();
CreateWebVrSubtree();
CreateKeyboard();
CreateController();
}
void UiSceneCreator::Create2dBrowsingSubtreeRoots() {
auto element = Create<UiElement>(k2dBrowsingRoot, kPhaseNone);
element->set_hit_testable(false);
element->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) {
bool ready = !m->background_available ||
(m->background_available && m->background_loaded);
return m->browsing_enabled() && ready;
},
base::Unretained(model_)),
VR_BIND_LAMBDA([](UiElement* e, const bool& v) { e->SetVisible(v); },
base::Unretained(element.get()))));
scene_->AddUiElement(kRoot, std::move(element));
element = Create<UiElement>(k2dBrowsingBackground, kPhaseNone);
element->set_hit_testable(false);
scene_->AddUiElement(k2dBrowsingRoot, std::move(element));
auto repositioner = std::make_unique<Repositioner>(kContentDistance);
repositioner->SetName(k2dBrowsingRepositioner);
repositioner->AddBinding(
VR_BIND_FUNC(bool, Model, model_, model->reposition_window_enabled(),
Repositioner, repositioner.get(), set_enable));
repositioner->AddBinding(
VR_BIND_FUNC(gfx::Point3F, Model, model_, model->controller.laser_origin,
Repositioner, repositioner.get(), set_laser_origin));
repositioner->AddBinding(VR_BIND_FUNC(
gfx::Vector3dF, Model, model_, model->controller.laser_direction,
Repositioner, repositioner.get(), set_laser_direction));
scene_->AddUiElement(k2dBrowsingRoot, std::move(repositioner));
element = Create<UiElement>(k2dBrowsingVisibiltyControlForVoice, kPhaseNone);
element->set_hit_testable(false);
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(element));
element = Create<UiElement>(k2dBrowsingVisibiltyControlForSiteInfoPrompt,
kPhaseNone);
element->set_hit_testable(false);
element->AddBinding(VR_BIND_FUNC(
bool, Model, model_,
model->active_modal_prompt_type != kModalPromptTypeExitVRForSiteInfo,
UiElement, element.get(), SetVisible));
scene_->AddUiElement(k2dBrowsingVisibiltyControlForVoice, std::move(element));
element = Create<UiElement>(k2dBrowsingOpacityControlForAudioPermissionPrompt,
kPhaseNone);
element->set_hit_testable(false);
element->AddBinding(
VR_BIND(bool, Model, model_,
model->active_modal_prompt_type !=
kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission,
UiElement, element.get(),
view->SetOpacity(value ? 1.0 : kModalPromptFadeOpacity)));
scene_->AddUiElement(k2dBrowsingVisibiltyControlForSiteInfoPrompt,
std::move(element));
element = Create<UiElement>(k2dBrowsingForeground, kPhaseNone);
element->set_hit_testable(false);
element->SetTransitionedProperties({OPACITY});
element->SetTransitionDuration(base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
VR_BIND_VISIBILITY(element, model->default_browsing_enabled() ||
model->fullscreen_enabled());
scene_->AddUiElement(k2dBrowsingOpacityControlForAudioPermissionPrompt,
std::move(element));
element = Create<UiElement>(k2dBrowsingContentGroup, kPhaseNone);
element->SetTranslate(0, kContentVerticalOffset, -kContentDistance);
element->SetSize(kContentWidth, kContentHeight);
element->set_hit_testable(false);
element->SetTransitionedProperties({TRANSFORM});
element->AddBinding(
VR_BIND(bool, Model, model_, model->fullscreen_enabled(), UiElement,
element.get(),
view->SetTranslate(
0, value ? kFullscreenVerticalOffset : kContentVerticalOffset,
value ? -kFullscreenDistance : -kContentDistance)));
scene_->AddUiElement(k2dBrowsingForeground, std::move(element));
}
void UiSceneCreator::CreateWebVrRoot() {
auto element = std::make_unique<UiElement>();
element->SetName(kWebVrRoot);
element->set_hit_testable(false);
VR_BIND_VISIBILITY(element, model->web_vr_enabled());
scene_->AddUiElement(kRoot, std::move(element));
}
void UiSceneCreator::CreateExitWarning() {
auto scrim = std::make_unique<FullScreenRect>();
scrim->SetName(kScreenDimmer);
scrim->SetDrawPhase(kPhaseForeground);
scrim->SetVisible(false);
scrim->set_hit_testable(false);
scrim->SetOpacity(kScreenDimmerOpacity);
scrim->SetCenterColor(model_->color_scheme().dimmer_inner);
scrim->SetEdgeColor(model_->color_scheme().dimmer_outer);
VR_BIND_VISIBILITY(scrim, model->exiting_vr);
scene_->AddUiElement(k2dBrowsingViewportAwareRoot, std::move(scrim));
// Create transient exit warning.
auto scaler = std::make_unique<ScaledDepthAdjuster>(kExitWarningDistance);
auto exit_warning_text = std::make_unique<Text>(kExitWarningFontHeightDMM);
exit_warning_text->SetName(kExitWarningText);
exit_warning_text->SetDrawPhase(kPhaseForeground);
exit_warning_text->SetText(
l10n_util::GetStringUTF16(IDS_VR_BROWSER_UNSUPPORTED_PAGE));
exit_warning_text->SetSize(kExitWarningTextWidthDMM, 0);
exit_warning_text->SetVisible(true);
exit_warning_text->set_hit_testable(false);
VR_BIND_COLOR(model_, exit_warning_text.get(),
&ColorScheme::exit_warning_foreground, &Text::SetColor);
auto exit_warning_bg = std::make_unique<Rect>();
exit_warning_bg->SetName(kExitWarningBackground);
exit_warning_bg->SetDrawPhase(kPhaseForeground);
exit_warning_bg->set_bounds_contain_children(true);
exit_warning_bg->set_padding(kExitWarningXPaddingDMM,
kExitWarningYPaddingDMM);
exit_warning_bg->set_corner_radius(kExitWarningCornerRadiusDMM);
exit_warning_bg->set_hit_testable(false);
exit_warning_bg->AddChild(std::move(exit_warning_text));
VR_BIND_VISIBILITY(exit_warning_bg, model->exiting_vr);
VR_BIND_COLOR(model_, exit_warning_bg.get(),
&ColorScheme::exit_warning_background, &Rect::SetColor);
scaler->AddChild(std::move(exit_warning_bg));
scene_->AddUiElement(k2dBrowsingViewportAwareRoot, std::move(scaler));
}
void UiSceneCreator::CreateSystemIndicators() {
struct Indicator {
UiElementName name;
const gfx::VectorIcon& icon;
int resource_string;
bool PermissionsModel::*signal;
};
const std::vector<Indicator> indicators = {
{kAudioCaptureIndicator, vector_icons::kMicIcon,
IDS_AUDIO_CALL_NOTIFICATION_TEXT_2,
&PermissionsModel::audio_capture_enabled},
{kVideoCaptureIndicator, vector_icons::kVideocamIcon,
IDS_VIDEO_CALL_NOTIFICATION_TEXT_2,
&PermissionsModel::video_capture_enabled},
{kScreenCaptureIndicator, vector_icons::kScreenShareIcon,
IDS_SCREEN_CAPTURE_NOTIFICATION_TEXT_2,
&PermissionsModel::screen_capture_enabled},
{kBluetoothConnectedIndicator, vector_icons::kBluetoothConnectedIcon, 0,
&PermissionsModel::bluetooth_connected},
{kLocationAccessIndicator, vector_icons::kLocationOnIcon, 0,
&PermissionsModel::location_access},
};
std::unique_ptr<LinearLayout> indicator_layout =
std::make_unique<LinearLayout>(LinearLayout::kRight);
indicator_layout->SetName(kIndicatorLayout);
indicator_layout->set_hit_testable(false);
indicator_layout->set_y_anchoring(TOP);
indicator_layout->SetTranslate(0, kIndicatorVerticalOffset,
kIndicatorDistanceOffset);
indicator_layout->set_margin(kIndicatorGap);
VR_BIND_VISIBILITY(indicator_layout, !model->fullscreen_enabled());
for (const auto& indicator : indicators) {
auto element = std::make_unique<Toast>();
element->SetName(indicator.name);
element->SetDrawPhase(kPhaseForeground);
element->set_padding(kIndicatorXPadding, kIndicatorYPadding);
element->set_corner_radius(kIndicatorCornerRadius);
element->SetMargin(kIndicatorMargin);
element->AddIcon(indicator.icon, 64, kIndicatorIconSize);
if (indicator.resource_string != 0) {
element->AddText(l10n_util::GetStringUTF16(indicator.resource_string),
kIndicatorFontHeightDmm,
TextLayoutMode::kSingleLineFixedHeight);
}
VR_BIND_COLOR(model_, element.get(),
&ColorScheme::system_indicator_background,
&Toast::SetBackgroundColor);
VR_BIND_COLOR(model_, element.get(),
&ColorScheme::system_indicator_foreground,
&Toast::SetForegroundColor);
element->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m, bool PermissionsModel::*permission) {
return m->permissions.*permission;
},
base::Unretained(model_), indicator.signal),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& v) {
e->SetVisible(v);
e->set_requires_layout(v);
},
base::Unretained(element.get()))));
indicator_layout->AddChild(std::move(element));
}
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(indicator_layout));
}
void UiSceneCreator::CreateContentQuad() {
// Place an invisible but hittable plane behind the content quad, to keep the
// reticle roughly planar with the content if near content.
auto hit_plane = std::make_unique<InvisibleHitTarget>();
hit_plane->SetName(kBackplane);
hit_plane->SetDrawPhase(kPhaseForeground);
hit_plane->SetSize(kBackplaneSize, kSceneHeight);
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(hit_plane));
auto shadow = Create<Shadow>(kContentQuadShadow, kPhaseForeground);
shadow->set_intensity(kContentShadowIntesity);
shadow->SetTranslate(0, 0, -kContentShadowOffset);
auto main_content = std::make_unique<ContentElement>(
content_input_delegate_,
base::BindRepeating(&UiBrowserInterface::OnContentScreenBoundsChanged,
base::Unretained(browser_)));
main_content->SetName(kContentQuad);
main_content->SetDrawPhase(kPhaseForeground);
main_content->SetSize(kContentWidth, kContentHeight);
main_content->set_corner_radius(kContentCornerRadius);
main_content->SetTranslate(0, 0, kContentShadowOffset);
main_content->SetTransitionedProperties({BOUNDS});
main_content->AddBinding(
VR_BIND(bool, Model, model_, model->fullscreen_enabled(), UiElement,
main_content.get(),
view->SetSize(value ? kFullscreenWidth : kContentWidth,
value ? kFullscreenHeight : kContentHeight)));
main_content->AddBinding(
VR_BIND_FUNC(gfx::Transform, Model, model_, model->projection_matrix,
ContentElement, main_content.get(), SetProjectionMatrix));
main_content->AddBinding(
VR_BIND_FUNC(unsigned int, Model, model_, model->content_texture_id,
ContentElement, main_content.get(), SetTextureId));
main_content->AddBinding(VR_BIND_FUNC(UiElementRenderer::TextureLocation,
Model, model_, model->content_location,
ContentElement, main_content.get(),
SetTextureLocation));
shadow->AddChild(std::move(main_content));
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(shadow));
// Limit reticle distance to a sphere based on maximum content distance.
scene_->set_background_distance(kFullscreenDistance *
kBackgroundDistanceMultiplier);
}
void UiSceneCreator::CreateWebVrSubtree() {
CreateSplashScreenForDirectWebVrLaunch();
CreateWebVrOverlayElements();
CreateWebVrTimeoutScreen();
// Note, this cannot be a descendant of the viewport aware root, otherwise it
// will fade out when the viewport aware elements reposition.
auto bg = std::make_unique<FullScreenRect>();
bg->SetName(kWebVrBackground);
bg->SetDrawPhase(kPhaseBackground);
bg->SetVisible(false);
bg->set_hit_testable(false);
bg->SetColor(model_->color_scheme().web_vr_background);
bg->SetTransitionedProperties({OPACITY});
VR_BIND_VISIBILITY(
bg, model->web_vr_enabled() && !model->web_vr.has_produced_frames());
scene_->AddUiElement(kWebVrRoot, std::move(bg));
}
void UiSceneCreator::CreateSplashScreenForDirectWebVrLaunch() {
// Create transient parent.
// TODO(crbug.com/762074): We should timeout after some time and show an
// error if the user is stuck on the splash screen.
auto transient_parent = std::make_unique<ShowUntilSignalTransientElement>(
base::TimeDelta::FromSeconds(kSplashScreenMinDurationSeconds),
base::TimeDelta::Max(),
base::BindRepeating(
[](Model* model) {
DCHECK(model->web_vr.awaiting_min_splash_screen_duration());
// TODO(ymalik): The assumption here is that the WebVR VSync will be
// paused until the min splash screen duration passes. This state
// change should be driven by the scheduler in the future and the UI
// should act on it.
model->web_vr.state = kWebVrAwaitingFirstFrame;
},
base::Unretained(model_)),
base::BindRepeating(
[](Model* model, UiBrowserInterface* browser,
TransientElementHideReason reason) {
if (reason == TransientElementHideReason::kTimeout) {
browser->ExitPresent();
}
},
base::Unretained(model_), base::Unretained(browser_)));
transient_parent->SetName(kSplashScreenTransientParent);
transient_parent->set_hit_testable(false);
transient_parent->SetTransitionedProperties({OPACITY});
VR_BIND_VISIBILITY(transient_parent,
model->web_vr_autopresentation_enabled());
transient_parent->AddBinding(VR_BIND_FUNC(
bool, Model, model_,
model->web_vr_autopresentation_enabled() &&
model->web_vr.state > kWebVrAwaitingFirstFrame,
ShowUntilSignalTransientElement, transient_parent.get(), Signal));
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(transient_parent));
// Add "Running in Chrome" text.
auto text_scaler =
std::make_unique<ScaledDepthAdjuster>(kSplashScreenTextDistance);
auto text = std::make_unique<Text>(kSplashScreenTextFontHeightDMM);
VR_BIND_COLOR(model_, text.get(), &ColorScheme::splash_screen_text_color,
&Text::SetColor);
text->SetText(l10n_util::GetStringUTF16(IDS_VR_RUNNING_IN_CHROME_MESSAGE));
text->SetName(kSplashScreenText);
text->SetDrawPhase(kPhaseForeground);
text->set_hit_testable(false);
text->SetSize(kSplashScreenTextWidthDMM, 0);
text->SetTranslate(0, kSplashScreenTextVerticalOffsetDMM, 0);
text_scaler->AddChild(std::move(text));
scene_->AddUiElement(kSplashScreenTransientParent, std::move(text_scaler));
}
void UiSceneCreator::CreateWebVrTimeoutScreen() {
auto scaler = std::make_unique<ScaledDepthAdjuster>(kTimeoutScreenDisatance);
scaler->SetName(kWebVrTimeoutRoot);
scaler->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* model, UiElement* splash_screen) {
// The timeout UI should only be visible when the splash screen is
// not visible.
return (model->web_vr.state == kWebVrTimeoutImminent ||
model->web_vr.state == kWebVrTimedOut) &&
splash_screen->GetTargetOpacity() == 0.f;
},
base::Unretained(model_),
base::Unretained(
scene_->GetUiElementByName(kSplashScreenTransientParent))),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& value) { e->SetVisible(value); },
base::Unretained(scaler.get()))));
auto spinner = std::make_unique<Spinner>(512);
spinner->SetName(kWebVrTimeoutSpinner);
spinner->SetDrawPhase(kPhaseForeground);
spinner->SetTransitionedProperties({OPACITY});
spinner->SetVisible(false);
spinner->SetSize(kTimeoutSpinnerSizeDMM, kTimeoutSpinnerSizeDMM);
spinner->SetTranslate(0, kTimeoutSpinnerVerticalOffsetDMM, 0);
spinner->SetColor(model_->color_scheme().web_vr_timeout_spinner);
VR_BIND_VISIBILITY(spinner, model->web_vr.state == kWebVrTimeoutImminent);
auto timeout_message = Create<Rect>(kWebVrTimeoutMessage, kPhaseForeground);
timeout_message->SetVisible(false);
timeout_message->set_bounds_contain_children(true);
timeout_message->set_corner_radius(kTimeoutMessageCornerRadiusDMM);
timeout_message->SetTransitionedProperties({OPACITY, TRANSFORM});
timeout_message->set_padding(kTimeoutMessageHorizontalPaddingDMM,
kTimeoutMessageVerticalPaddingDMM);
VR_BIND_VISIBILITY(timeout_message, model->web_vr.state == kWebVrTimedOut);
timeout_message->SetColor(
model_->color_scheme().web_vr_timeout_message_background);
auto timeout_layout = Create<LinearLayout>(kWebVrTimeoutMessageLayout,
kPhaseNone, LinearLayout::kRight);
timeout_layout->set_hit_testable(false);
timeout_layout->set_margin(kTimeoutMessageLayoutGapDMM);
auto timeout_icon =
Create<VectorIcon>(kWebVrTimeoutMessageIcon, kPhaseForeground, 512);
timeout_icon->SetIcon(kSadTabIcon);
timeout_icon->SetSize(kTimeoutMessageIconWidthDMM,
kTimeoutMessageIconHeightDMM);
auto timeout_text = Create<Text>(kWebVrTimeoutMessageText, kPhaseForeground,
kTimeoutMessageTextFontHeightDMM);
timeout_text->SetText(
l10n_util::GetStringUTF16(IDS_VR_WEB_VR_TIMEOUT_MESSAGE));
timeout_text->SetColor(
model_->color_scheme().web_vr_timeout_message_foreground);
timeout_text->SetAlignment(UiTexture::kTextAlignmentLeft);
timeout_text->SetSize(kTimeoutMessageTextWidthDMM,
kTimeoutMessageTextHeightDMM);
auto button_scaler =
std::make_unique<ScaledDepthAdjuster>(kTimeoutButtonDepthOffset);
auto button =
Create<DiscButton>(kWebVrTimeoutMessageButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::ExitPresent,
base::Unretained(browser_)),
vector_icons::kClose16Icon);
button->SetVisible(false);
button->SetTranslate(0, -kTimeoutMessageTextWidthDMM, 0);
button->SetRotate(1, 0, 0, kTimeoutButtonRotationRad);
button->SetTransitionedProperties({OPACITY});
button->SetSize(kWebVrTimeoutMessageButtonDiameterDMM,
kWebVrTimeoutMessageButtonDiameterDMM);
VR_BIND_VISIBILITY(button, model->web_vr.state == kWebVrTimedOut);
VR_BIND_BUTTON_COLORS(model_, button.get(), &ColorScheme::button_colors,
&DiscButton::SetButtonColors);
auto timeout_button_text =
Create<Text>(kWebVrTimeoutMessageButtonText, kPhaseForeground,
kTimeoutMessageTextFontHeightDMM);
// Disk-style button text is not uppercase. See crbug.com/787654.
timeout_button_text->SetText(
l10n_util::GetStringUTF16(IDS_VR_WEB_VR_EXIT_BUTTON_LABEL));
timeout_button_text->SetColor(model_->color_scheme().web_vr_timeout_spinner);
timeout_button_text->SetSize(kTimeoutButtonTextWidthDMM,
kTimeoutButtonTextHeightDMM);
timeout_button_text->set_y_anchoring(BOTTOM);
timeout_button_text->SetTranslate(0, -kTimeoutButtonTextVerticalOffsetDMM, 0);
button->AddChild(std::move(timeout_button_text));
timeout_layout->AddChild(std::move(timeout_icon));
timeout_layout->AddChild(std::move(timeout_text));
timeout_message->AddChild(std::move(timeout_layout));
button_scaler->AddChild(std::move(button));
timeout_message->AddChild(std::move(button_scaler));
scaler->AddChild(std::move(timeout_message));
scaler->AddChild(std::move(spinner));
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(scaler));
}
void UiSceneCreator::CreateUnderDevelopmentNotice() {
auto text = std::make_unique<Text>(kUnderDevelopmentNoticeFontHeightDMM);
VR_BIND_COLOR(model_, text.get(), &ColorScheme::world_background_text,
&Text::SetColor);
text->SetText(l10n_util::GetStringUTF16(IDS_VR_UNDER_DEVELOPMENT_NOTICE));
text->SetName(kUnderDevelopmentNotice);
text->SetDrawPhase(kPhaseForeground);
text->set_hit_testable(false);
text->SetSize(kUnderDevelopmentNoticeWidthDMM,
kUnderDevelopmentNoticeHeightDMM);
text->SetTranslate(0, -kUnderDevelopmentNoticeVerticalOffsetDMM, 0);
text->SetRotate(1, 0, 0, kUnderDevelopmentNoticeRotationRad);
text->set_y_anchoring(BOTTOM);
text->set_contributes_to_parent_bounds(false);
scene_->AddUiElement(kUrlBar, std::move(text));
}
void UiSceneCreator::CreateBackground() {
// Textured background.
auto background =
Create<Background>(k2dBrowsingTexturedBackground, kPhaseBackground);
background->SetVisible(true);
VR_BIND_VISIBILITY(background, model->background_loaded);
background->AddBinding(
VR_BIND_FUNC(float, Model, model_, model->color_scheme().normal_factor,
Background, background.get(), SetNormalFactor));
background->AddBinding(
VR_BIND_FUNC(float, Model, model_, model->color_scheme().incognito_factor,
Background, background.get(), SetIncognitoFactor));
background->AddBinding(VR_BIND_FUNC(
float, Model, model_, model->color_scheme().fullscreen_factor, Background,
background.get(), SetFullscreenFactor));
scene_->AddUiElement(k2dBrowsingBackground, std::move(background));
auto element = Create<UiElement>(k2dBrowsingDefaultBackground, kPhaseNone);
element->set_hit_testable(false);
VR_BIND_VISIBILITY(element, !model->background_loaded);
scene_->AddUiElement(k2dBrowsingBackground, std::move(element));
// Background solid-color panels.
struct Panel {
UiElementName name;
int x_offset;
int y_offset;
int z_offset;
int x_rotation;
int y_rotation;
int angle;
};
const std::vector<Panel> panels = {
{kBackgroundFront, 0, 0, -1, 0, 1, 0},
{kBackgroundLeft, -1, 0, 0, 0, 1, 1},
{kBackgroundBack, 0, 0, 1, 0, 1, 2},
{kBackgroundRight, 1, 0, 0, 0, 1, 3},
{kBackgroundTop, 0, 1, 0, 1, 0, 1},
{kBackgroundBottom, 0, -1, 0, 1, 0, -1},
};
for (auto& panel : panels) {
auto panel_element = Create<Rect>(panel.name, kPhaseBackground);
panel_element->SetSize(kSceneSize, kSceneSize);
panel_element->SetTranslate(panel.x_offset * kSceneSize / 2,
panel.y_offset * kSceneSize / 2,
panel.z_offset * kSceneSize / 2);
panel_element->SetRotate(panel.x_rotation, panel.y_rotation, 0,
base::kPiFloat / 2 * panel.angle);
panel_element->set_hit_testable(false);
VR_BIND_COLOR(model_, panel_element.get(), &ColorScheme::world_background,
&Rect::SetColor);
VR_BIND_VISIBILITY(panel_element, model->browsing_enabled());
scene_->AddUiElement(k2dBrowsingDefaultBackground,
std::move(panel_element));
}
auto stars = Create<Stars>(kStars, kPhaseBackground);
stars->SetRotate(1, 0, 0, base::kPiFloat * 0.5);
scene_->AddUiElement(k2dBrowsingTexturedBackground, std::move(stars));
auto grid = Create<Grid>(kNone, kPhaseBackground);
grid->SetSize(kSceneSize, kSceneSize);
grid->SetTranslate(0.0, -kSceneHeight / 2, 0.0);
grid->SetRotate(1, 0, 0, -base::kPiFloat / 2);
grid->set_gridline_count(kFloorGridlineCount);
grid->SetEdgeColor(SK_ColorTRANSPARENT);
grid->SetCenterColor(SK_ColorTRANSPARENT);
grid->SetGridColor(model_->color_scheme().floor_grid);
grid->set_hit_testable(false);
grid->SetOpacity(kGridOpacity);
scene_->AddUiElement(k2dBrowsingTexturedBackground, std::move(grid));
auto floor = Create<Grid>(kFloor, kPhaseBackground);
floor->SetSize(kSceneSize, kSceneSize);
floor->SetTranslate(0.0, -kSceneHeight / 2, 0.0);
floor->SetRotate(1, 0, 0, -base::kPiFloat / 2);
floor->set_gridline_count(kFloorGridlineCount);
floor->set_focusable(false);
VR_BIND_COLOR(model_, floor.get(), &ColorScheme::floor,
&Grid::SetCenterColor);
VR_BIND_COLOR(model_, floor.get(), &ColorScheme::world_background,
&Grid::SetEdgeColor);
VR_BIND_COLOR(model_, floor.get(), &ColorScheme::floor_grid,
&Grid::SetGridColor);
scene_->AddUiElement(k2dBrowsingDefaultBackground, std::move(floor));
// Ceiling.
auto ceiling = Create<Rect>(kCeiling, kPhaseBackground);
ceiling->set_focusable(false);
ceiling->SetSize(kSceneSize, kSceneSize);
ceiling->SetTranslate(0.0, kSceneHeight / 2, 0.0);
ceiling->SetRotate(1, 0, 0, base::kPiFloat / 2);
VR_BIND_COLOR(model_, ceiling.get(), &ColorScheme::ceiling,
&Rect::SetCenterColor);
VR_BIND_COLOR(model_, ceiling.get(), &ColorScheme::world_background,
&Rect::SetEdgeColor);
scene_->AddUiElement(k2dBrowsingDefaultBackground, std::move(ceiling));
}
void UiSceneCreator::CreateViewportAwareRoot() {
auto element = std::make_unique<ViewportAwareRoot>();
element->SetName(kWebVrViewportAwareRoot);
element->set_hit_testable(false);
scene_->AddUiElement(kWebVrRoot, std::move(element));
element = std::make_unique<ViewportAwareRoot>();
element->SetName(k2dBrowsingViewportAwareRoot);
element->set_hit_testable(false);
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(element));
}
void UiSceneCreator::CreateVoiceSearchUiGroup() {
auto voice_search_button = Create<DiscButton>(
kVoiceSearchButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::SetVoiceSearchActive,
base::Unretained(browser_), true),
vector_icons::kMicIcon);
voice_search_button->SetSize(kVoiceSearchButtonDiameterDMM,
kVoiceSearchButtonDiameterDMM);
voice_search_button->set_hover_offset(kButtonZOffsetHoverDMM);
voice_search_button->SetTranslate(0.f, -kVoiceSearchButtonYOffsetDMM, 0.f);
voice_search_button->set_y_anchoring(BOTTOM);
voice_search_button->set_y_centering(TOP);
voice_search_button->set_contributes_to_parent_bounds(false);
VR_BIND_VISIBILITY(
voice_search_button,
model->speech.has_or_can_request_audio_permission && !model->incognito);
VR_BIND_BUTTON_COLORS(model_, voice_search_button.get(),
&ColorScheme::button_colors,
&DiscButton::SetButtonColors);
scene_->AddUiElement(kUrlBar, std::move(voice_search_button));
auto speech_recognition_root = std::make_unique<UiElement>();
speech_recognition_root->SetName(kSpeechRecognitionRoot);
speech_recognition_root->SetTranslate(0.f, 0.f, -kContentDistance);
speech_recognition_root->set_hit_testable(false);
speech_recognition_root->SetTransitionedProperties({OPACITY});
speech_recognition_root->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
// Set initial visibility so we don't see the voice search ui fade out.
speech_recognition_root->SetVisibleImmediately(false);
scene_->AddUiElement(k2dBrowsingRepositioner,
std::move(speech_recognition_root));
auto inner_circle = std::make_unique<Rect>();
inner_circle->SetName(kSpeechRecognitionCircle);
inner_circle->SetDrawPhase(kPhaseForeground);
inner_circle->SetSize(kCloseButtonDiameter * 2, kCloseButtonDiameter * 2);
inner_circle->set_corner_radius(kCloseButtonDiameter);
inner_circle->set_hit_testable(false);
VR_BIND_COLOR(model_, inner_circle.get(),
&ColorScheme::speech_recognition_circle_background,
&Rect::SetColor);
scene_->AddUiElement(kSpeechRecognitionRoot, std::move(inner_circle));
auto microphone_icon = std::make_unique<VectorIcon>(512);
microphone_icon->SetIcon(vector_icons::kMicIcon);
microphone_icon->SetName(kSpeechRecognitionMicrophoneIcon);
microphone_icon->SetDrawPhase(kPhaseForeground);
microphone_icon->set_hit_testable(false);
microphone_icon->SetSize(kCloseButtonDiameter, kCloseButtonDiameter);
scene_->AddUiElement(kSpeechRecognitionRoot, std::move(microphone_icon));
auto speech_result_parent = CreateTransientParent(
kSpeechRecognitionResult, kSpeechRecognitionResultTimeoutSeconds, false);
// We need to explicitly set the initial visibility of
// kSpeechRecognitionResult as k2dBrowsingForeground's visibility depends on
// it in a binding. However, k2dBrowsingForeground's binding updated before
// kSpeechRecognitionResult. So the initial value needs to be correctly set
// instead of depend on binding to kick in.
speech_result_parent->SetVisibleImmediately(false);
speech_result_parent->SetTransitionedProperties({OPACITY});
speech_result_parent->SetTransitionDuration(base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
speech_result_parent->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) { return m->speech.recognition_result.empty(); },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& v) {
if (v) {
e->SetVisible(false);
} else {
e->SetVisibleImmediately(true);
}
},
speech_result_parent.get())));
auto speech_result =
std::make_unique<Text>(kVoiceSearchRecognitionResultTextHeight);
speech_result->SetName(kSpeechRecognitionResultText);
speech_result->SetDrawPhase(kPhaseForeground);
speech_result->SetTranslate(0.f, kSpeechRecognitionResultTextYOffset, 0.f);
speech_result->set_hit_testable(false);
speech_result->SetSize(kVoiceSearchRecognitionResultTextWidth, 0);
speech_result->SetAlignment(UiTexture::kTextAlignmentCenter);
VR_BIND_COLOR(model_, speech_result.get(), &ColorScheme::prompt_foreground,
&Text::SetColor);
speech_result->AddBinding(VR_BIND_FUNC(base::string16, Model, model_,
model->speech.recognition_result, Text,
speech_result.get(), SetText));
speech_result_parent->AddChild(std::move(speech_result));
auto hit_target = std::make_unique<InvisibleHitTarget>();
hit_target->SetName(kSpeechRecognitionResultBackplane);
hit_target->SetDrawPhase(kPhaseForeground);
hit_target->SetSize(kPromptBackplaneSize, kPromptBackplaneSize);
speech_result_parent->AddChild(std::move(hit_target));
auto speech_recognition_listening = std::make_unique<UiElement>();
UiElement* listening_ui_root = speech_recognition_listening.get();
speech_recognition_listening->SetName(kSpeechRecognitionListening);
speech_recognition_listening->set_hit_testable(false);
// We need to explicitly set the initial visibility of this element for the
// same reason as kSpeechRecognitionResult.
speech_recognition_listening->SetVisibleImmediately(false);
speech_recognition_listening->SetTransitionedProperties({OPACITY});
speech_recognition_listening->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
speech_recognition_listening->AddBinding(
std::make_unique<Binding<std::pair<bool, float>>>(
VR_BIND_LAMBDA(
[](Model* m, UiElement* result_parent) {
return std::pair<bool, float>(
m->voice_search_enabled(),
result_parent->GetTargetOpacity());
},
base::Unretained(model_),
base::Unretained(speech_result_parent.get())),
VR_BIND_LAMBDA(
[](UiElement* listening, const std::pair<bool, float>& value) {
if (!value.first && value.second != 0.f) {
listening->SetVisibleImmediately(false);
} else {
listening->SetVisible(value.first);
}
},
base::Unretained(listening_ui_root))));
scene_->AddUiElement(kSpeechRecognitionRoot,
std::move(speech_recognition_listening));
scene_->AddUiElement(kSpeechRecognitionRoot, std::move(speech_result_parent));
auto growing_circle = std::make_unique<Throbber>();
growing_circle->SetName(kSpeechRecognitionListeningGrowingCircle);
growing_circle->SetDrawPhase(kPhaseForeground);
growing_circle->SetSize(kCloseButtonDiameter * 2, kCloseButtonDiameter * 2);
growing_circle->set_corner_radius(kCloseButtonDiameter);
growing_circle->set_hit_testable(false);
VR_BIND_COLOR(model_, growing_circle.get(),
&ColorScheme::speech_recognition_circle_background,
&Rect::SetColor);
growing_circle->AddBinding(
VR_BIND(int, Model, model_, model->speech.speech_recognition_state,
Throbber, growing_circle.get(),
view->SetCircleGrowAnimationEnabled(
value == SPEECH_RECOGNITION_IN_SPEECH ||
value == SPEECH_RECOGNITION_RECOGNIZING ||
value == SPEECH_RECOGNITION_READY)));
scene_->AddUiElement(kSpeechRecognitionListening, std::move(growing_circle));
auto close_button = Create<DiscButton>(
kSpeechRecognitionListeningCloseButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::SetVoiceSearchActive,
base::Unretained(browser_), false),
vector_icons::kClose16Icon);
close_button->SetSize(kVoiceSearchCloseButtonWidth,
kVoiceSearchCloseButtonHeight);
close_button->set_hover_offset(kButtonZOffsetHoverDMM * kContentDistance);
close_button->SetTranslate(0.0, -kVoiceSearchCloseButtonYOffset, 0.f);
VR_BIND_BUTTON_COLORS(model_, close_button.get(), &ColorScheme::button_colors,
&DiscButton::SetButtonColors);
scene_->AddUiElement(kSpeechRecognitionListening, std::move(close_button));
auto* root = scene_->GetUiElementByName(kSpeechRecognitionRoot);
root->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* model, UiElement* speech_listening,
UiElement* speech_result_parent) {
// The speech recognition root should be visible ad long as the
// speech listening or result subtree is visibe.
return model->voice_search_enabled() ||
speech_listening->GetTargetOpacity() != 0.f ||
speech_result_parent->GetTargetOpacity() != 0.f;
},
base::Unretained(model_),
base::Unretained(
scene_->GetUiElementByName(kSpeechRecognitionListening)),
base::Unretained(
scene_->GetUiElementByName(kSpeechRecognitionResult))),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& value) { e->SetVisible(value); },
base::Unretained(root))));
BIND_VISIBILITY_CONTROL_FOR_VOICE(
scene_->GetUiElementByName(k2dBrowsingVisibiltyControlForVoice), model_,
browsing_enabled());
BIND_VISIBILITY_CONTROL_FOR_VOICE(
scene_->GetUiElementByName(kOmniboxVisibiltyControlForVoice), model_,
omnibox_editing_enabled());
}
void UiSceneCreator::CreateController() {
auto root = std::make_unique<UiElement>();
root->SetName(kControllerRoot);
root->set_hit_testable(false);
VR_BIND_VISIBILITY(
root, model->browsing_enabled() || model->web_vr.state == kWebVrTimedOut);
scene_->AddUiElement(kRoot, std::move(root));
auto group = std::make_unique<UiElement>();
group->SetName(kControllerGroup);
group->set_hit_testable(false);
group->SetTransitionedProperties({OPACITY});
group->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) {
return !m->controller.quiescent || !m->skips_redraw_when_not_dirty;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& visible) {
e->SetTransitionDuration(base::TimeDelta::FromMilliseconds(
visible ? kControllerFadeInMs : kControllerFadeOutMs));
e->SetVisible(visible);
},
base::Unretained(group.get()))));
scene_->AddUiElement(kControllerRoot, std::move(group));
auto controller = std::make_unique<Controller>();
controller->SetDrawPhase(kPhaseForeground);
controller->AddBinding(VR_BIND_FUNC(gfx::Transform, Model, model_,
model->controller.transform, Controller,
controller.get(), set_local_transform));
controller->AddBinding(VR_BIND_FUNC(
bool, Model, model_,
model->controller.touchpad_button_state == UiInputManager::DOWN,
Controller, controller.get(), set_touchpad_button_pressed));
controller->AddBinding(
VR_BIND_FUNC(bool, Model, model_,
model->controller.app_button_state == UiInputManager::DOWN,
Controller, controller.get(), set_app_button_pressed));
controller->AddBinding(
VR_BIND_FUNC(bool, Model, model_,
model->controller.home_button_state == UiInputManager::DOWN,
Controller, controller.get(), set_home_button_pressed));
controller->AddBinding(VR_BIND_FUNC(float, Model, model_,
model->controller.opacity, Controller,
controller.get(), SetOpacity));
scene_->AddUiElement(kControllerGroup, std::move(controller));
auto laser = std::make_unique<Laser>(model_);
laser->SetDrawPhase(kPhaseForeground);
laser->AddBinding(VR_BIND_FUNC(float, Model, model_,
model->controller.opacity, Laser, laser.get(),
SetOpacity));
scene_->AddUiElement(kControllerGroup, std::move(laser));
auto reticle = std::make_unique<Reticle>(scene_, model_);
reticle->SetDrawPhase(kPhaseForeground);
scene_->AddUiElement(kControllerGroup, std::move(reticle));
}
std::unique_ptr<TextInput> UiSceneCreator::CreateTextInput(
float font_height_meters,
Model* model,
TextInputInfo* text_input_model,
TextInputDelegate* text_input_delegate) {
auto text_input = std::make_unique<TextInput>(
font_height_meters,
base::BindRepeating(
[](Model* model, bool focused) { model->editing_input = focused; },
base::Unretained(model)),
base::BindRepeating(
[](TextInputInfo* model, const TextInputInfo& text_input_info) {
*model = text_input_info;
},
base::Unretained(text_input_model)));
text_input->SetDrawPhase(kPhaseNone);
text_input->set_hit_testable(false);
text_input->SetTextInputDelegate(text_input_delegate);
text_input->AddBinding(std::make_unique<Binding<TextInputInfo>>(
VR_BIND_LAMBDA([](TextInputInfo* info) { return *info; },
base::Unretained(text_input_model)),
VR_BIND_LAMBDA([](TextInput* e,
const TextInputInfo& value) { e->UpdateInput(value); },
base::Unretained(text_input.get()))));
return text_input;
}
void UiSceneCreator::CreateKeyboard() {
auto visibility_control_root =
Create<UiElement>(kKeyboardVisibilityControlForVoice, kPhaseNone);
visibility_control_root->set_hit_testable(false);
BIND_VISIBILITY_CONTROL_FOR_VOICE(visibility_control_root.get(), model_,
editing_input);
auto scaler = std::make_unique<ScaledDepthAdjuster>(kKeyboardDistance);
scaler->SetName(kKeyboardDmmRoot);
auto keyboard = std::make_unique<Keyboard>();
keyboard->SetKeyboardDelegate(keyboard_delegate_);
keyboard->SetDrawPhase(kPhaseForeground);
keyboard->SetTranslate(0.0, kKeyboardVerticalOffsetDMM, 0.0);
VR_BIND_VISIBILITY(keyboard, model->editing_input);
scaler->AddChild(std::move(keyboard));
visibility_control_root->AddChild(std::move(scaler));
scene_->AddUiElement(k2dBrowsingRepositioner,
std::move(visibility_control_root));
}
void UiSceneCreator::CreateUrlBar() {
auto scaler = std::make_unique<ScaledDepthAdjuster>(kUrlBarDistance);
scaler->SetName(kUrlBarDmmRoot);
scene_->AddUiElement(k2dBrowsingForeground, std::move(scaler));
auto url_bar = Create<UiElement>(kUrlBar, kPhaseNone);
url_bar->set_hit_testable(false);
url_bar->SetTranslate(0, kUrlBarVerticalOffsetDMM, 0);
url_bar->SetRotate(1, 0, 0, kUrlBarRotationRad);
url_bar->set_bounds_contain_children(true);
VR_BIND_VISIBILITY(url_bar, !model->fullscreen_enabled());
scene_->AddUiElement(kUrlBarDmmRoot, std::move(url_bar));
auto layout =
Create<LinearLayout>(kUrlBarLayout, kPhaseNone, LinearLayout::kRight);
layout->set_hit_testable(false);
layout->set_bounds_contain_children(true);
scene_->AddUiElement(kUrlBar, std::move(layout));
auto back_button =
Create<Button>(kUrlBarBackButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::NavigateBack,
base::Unretained(browser_)));
back_button->SetSize(kUrlBarBackButtonWidthDMM, kUrlBarHeightDMM);
back_button->SetCornerRadii(
{kUrlBarHeightDMM / 2, 0, kUrlBarHeightDMM / 2, 0});
back_button->set_hover_offset(0.0f);
back_button->AddBinding(VR_BIND_FUNC(bool, Model, model_,
model->can_navigate_back, Button,
back_button.get(), set_enabled));
VR_BIND_BUTTON_COLORS(model_, back_button.get(), &ColorScheme::back_button,
&Button::SetButtonColors);
scene_->AddUiElement(kUrlBarLayout, std::move(back_button));
auto back_icon =
Create<VectorIcon>(kUrlBarBackButtonIcon, kPhaseForeground, 128);
back_icon->set_hit_testable(false);
back_icon->SetIcon(vector_icons::kBackArrowIcon);
back_icon->SetSize(kUrlBarIconSizeDMM, kUrlBarIconSizeDMM);
back_icon->SetTranslate(kUrlBarBackButtonIconOffsetDMM, 0, 0);
back_icon->AddBinding(VR_BIND_FUNC(
SkColor, Model, model_,
model->can_navigate_back
? model->color_scheme().button_colors.foreground
: model->color_scheme().button_colors.foreground_disabled,
VectorIcon, back_icon.get(), SetColor));
scene_->AddUiElement(kUrlBarBackButton, std::move(back_icon));
auto separator = Create<Rect>(kUrlBarSeparator, kPhaseForeground);
separator->SetSize(kUrlBarSeparatorWidthDMM, kUrlBarHeightDMM);
VR_BIND_COLOR(model_, separator.get(), &ColorScheme::url_bar_separator,
&Rect::SetColor);
scene_->AddUiElement(kUrlBarLayout, std::move(separator));
auto origin_region = Create<Rect>(kUrlBarOriginRegion, kPhaseForeground);
origin_region->SetSize(kUrlBarOriginRegionWidthDMM, kUrlBarHeightDMM);
origin_region->SetCornerRadii(
{0, kUrlBarHeightDMM / 2, 0, kUrlBarHeightDMM / 2});
VR_BIND_COLOR(model_, origin_region.get(), &ColorScheme::element_background,
&Rect::SetColor);
scene_->AddUiElement(kUrlBarLayout, std::move(origin_region));
base::RepeatingCallback<void()> url_click_callback;
if (base::FeatureList::IsEnabled(features::kVrBrowserKeyboard)) {
url_click_callback = base::BindRepeating(
[](Model* model) { model->push_mode(kModeEditingOmnibox); },
base::Unretained(model_));
} else {
url_click_callback = base::BindRepeating([] {});
}
auto origin_content = Create<UrlBar>(
kUrlBarOriginContent, kPhaseForeground, 512, url_click_callback,
base::BindRepeating(&UiBrowserInterface::OnUnsupportedMode,
base::Unretained(browser_)));
origin_content->SetSize(kUrlBarOriginContentWidthDMM, kUrlBarHeightDMM);
origin_content->SetTranslate(kUrlBarOriginContentOffsetDMM, 0, 0);
origin_content->set_x_anchoring(LEFT);
origin_content->set_x_centering(LEFT);
VR_BIND_VISIBILITY(origin_content, !model->fullscreen_enabled());
origin_content->AddBinding(
VR_BIND_FUNC(ToolbarState, Model, model_, model->toolbar_state, UrlBar,
origin_content.get(), SetToolbarState));
origin_content->AddBinding(VR_BIND_FUNC(UrlBarColors, Model, model_,
model->color_scheme().url_bar, UrlBar,
origin_content.get(), SetColors));
VR_BIND_COLOR(model_, origin_content.get(), &ColorScheme::element_background,
&TexturedElement::SetBackgroundColor);
scene_->AddUiElement(kUrlBarOriginRegion, std::move(origin_content));
}
void UiSceneCreator::CreateLoadingIndicator() {
auto indicator_bg = std::make_unique<Rect>();
indicator_bg->SetName(kLoadingIndicator);
indicator_bg->SetDrawPhase(kPhaseForeground);
indicator_bg->SetTranslate(0, kLoadingIndicatorVerticalOffsetDMM, 0);
indicator_bg->SetSize(kLoadingIndicatorWidthDMM, kLoadingIndicatorHeightDMM);
indicator_bg->set_y_anchoring(TOP);
indicator_bg->SetTransitionedProperties({OPACITY});
indicator_bg->set_corner_radius(kLoadingIndicatorHeightDMM * 0.5f);
indicator_bg->set_contributes_to_parent_bounds(false);
VR_BIND_VISIBILITY(indicator_bg, model->loading);
VR_BIND_COLOR(model_, indicator_bg.get(),
&ColorScheme::loading_indicator_background, &Rect::SetColor);
scene_->AddUiElement(kUrlBar, std::move(indicator_bg));
auto indicator_fg = std::make_unique<Rect>();
indicator_fg->SetDrawPhase(kPhaseForeground);
indicator_fg->SetName(kLoadingIndicatorForeground);
indicator_fg->set_x_anchoring(LEFT);
indicator_fg->set_corner_radius(kLoadingIndicatorHeightDMM * 0.5f);
indicator_fg->set_hit_testable(false);
VR_BIND_COLOR(model_, indicator_fg.get(),
&ColorScheme::loading_indicator_foreground, &Rect::SetColor);
indicator_fg->AddBinding(std::make_unique<Binding<float>>(
VR_BIND_LAMBDA([](Model* m) { return m->load_progress; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Rect* r, const float& value) {
r->SetSize(kLoadingIndicatorWidthDMM * value,
kLoadingIndicatorHeightDMM);
r->SetTranslate(kLoadingIndicatorWidthDMM * value * 0.5f, 0.0f,
0.001f);
},
base::Unretained(indicator_fg.get()))));
scene_->AddUiElement(kLoadingIndicator, std::move(indicator_fg));
}
void UiSceneCreator::CreateSnackbars() {
auto snackbar = CreateSnackbar(
kDownloadedSnackbar, model_, kFileDownloadDoneIcon,
l10n_util::GetStringUTF16(IDS_VR_COMPONENT_UPDATE_READY),
base::i18n::ToUpper(l10n_util::GetStringUTF16(IDS_VR_COMPONENT_APPLY)),
base::BindRepeating(
[](UiBrowserInterface* browser, Ui* ui) {
ui->OnAssetsLoading();
browser->LoadAssets();
},
base::Unretained(browser_), base::Unretained(ui_)));
snackbar->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return m->can_apply_new_background; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* s, const bool& value) {
s->SetVisible(value);
s->SetRotate(1, 0, 0, value ? 0 : kSnackbarMoveInAngle);
},
base::Unretained(snackbar.get()))));
snackbar->SetVisible(false);
snackbar->SetRotate(1, 0, 0, kSnackbarMoveInAngle);
snackbar->SetTransitionedProperties({OPACITY, TRANSFORM});
snackbar->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kSnackbarTransitionDurationMs));
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(snackbar));
}
void UiSceneCreator::CreateOmnibox() {
auto visibility_control_root =
Create<UiElement>(kOmniboxVisibiltyControlForVoice, kPhaseNone);
visibility_control_root->set_hit_testable(false);
auto scaler = std::make_unique<ScaledDepthAdjuster>(kUrlBarDistance);
scaler->SetName(kOmniboxDmmRoot);
auto visibility_toggle_for_audio_permission = Create<UiElement>(
kOmniboxVisibilityControlForAudioPermissionPrompt, kPhaseNone);
visibility_toggle_for_audio_permission->set_hit_testable(false);
// Note that wnen the audio permissions prompt is triggered from the omnibox
// editing mode, we don't change the opacity of the background like we do in
// the default browsing case.
visibility_toggle_for_audio_permission->AddBinding(VR_BIND_FUNC(
bool, Model, model_,
model->active_modal_prompt_type !=
kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission,
UiElement, visibility_toggle_for_audio_permission.get(), SetVisible));
auto omnibox_root = std::make_unique<UiElement>();
omnibox_root->SetName(kOmniboxRoot);
omnibox_root->SetDrawPhase(kPhaseNone);
omnibox_root->SetVisible(false);
omnibox_root->set_hit_testable(false);
omnibox_root->SetTransitionedProperties({OPACITY});
VR_BIND_VISIBILITY(omnibox_root, model->omnibox_editing_enabled());
auto shadow = std::make_unique<Shadow>();
shadow->SetName(kOmniboxShadow);
shadow->SetDrawPhase(kPhaseForeground);
shadow->set_intensity(kOmniboxShadowIntensity);
shadow->set_y_anchoring(TOP);
shadow->set_y_centering(BOTTOM);
shadow->set_corner_radius(kOmniboxCornerRadiusDMM);
auto omnibox_outer_layout = std::make_unique<LinearLayout>(LinearLayout::kUp);
omnibox_outer_layout->set_hit_testable(false);
omnibox_outer_layout->SetName(kOmniboxOuterLayout);
omnibox_outer_layout->SetTranslate(
0, kUrlBarVerticalOffsetDMM - 0.5f * kOmniboxHeightDMM,
kOmniboxShadowOffset);
omnibox_outer_layout->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return m->omnibox_editing_enabled(); },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& v) {
float y_offset =
v ? kOmniboxVerticalOffsetDMM : kUrlBarVerticalOffsetDMM;
y_offset -= 0.5 * kOmniboxHeightDMM;
e->SetTranslate(0, y_offset, kOmniboxShadowOffset);
},
omnibox_outer_layout.get())));
auto omnibox_outer_layout_spacer = Create<Rect>(kNone, kPhaseForeground);
omnibox_outer_layout_spacer->SetSize(kOmniboxWidthDMM, kSuggestionGapDMM);
VR_BIND_COLOR(model_, omnibox_outer_layout_spacer.get(),
&ColorScheme::url_bar_separator, &Rect::SetColor);
VR_BIND_VISIBILITY(omnibox_outer_layout_spacer,
!model->omnibox_suggestions.empty());
auto omnibox_container = std::make_unique<Rect>();
omnibox_container->SetName(kOmniboxContainer);
omnibox_container->SetDrawPhase(kPhaseForeground);
omnibox_container->SetSize(kOmniboxWidthDMM, kOmniboxHeightDMM);
omnibox_container->SetTransitionedProperties({TRANSFORM, OPACITY});
omnibox_container->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kOmniboxTransitionMs));
omnibox_container->set_focusable(false);
omnibox_container->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return m->omnibox_suggestions.empty(); },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Rect* r, const bool& v) {
if (v) {
r->SetCornerRadii(
{kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM,
kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM});
} else {
r->SetCornerRadii(
{0, 0, kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM});
}
},
omnibox_container.get())));
VR_BIND_COLOR(model_, omnibox_container.get(),
&ColorScheme::omnibox_background, &Rect::SetColor);
float width = kOmniboxWidthDMM - 2 * kOmniboxTextMarginDMM -
kOmniboxTextFieldIconSizeDMM;
auto omnibox_text_field =
CreateTextInput(kOmniboxTextHeightDMM, model_,
&model_->omnibox_text_field_info, text_input_delegate_);
omnibox_text_field->set_input_committed_callback(base::BindRepeating(
[](Model* model, UiBrowserInterface* browser, Ui* ui,
const TextInputInfo& text) {
if (!model->omnibox_suggestions.empty()) {
browser->Navigate(model->omnibox_suggestions.front().destination);
ui->OnUiRequestedNavigation();
}
},
base::Unretained(model_), base::Unretained(browser_),
base::Unretained(ui_)));
omnibox_text_field->AddBinding(VR_BIND(
TextInputInfo, Model, model_, model->omnibox_text_field_info,
UiBrowserInterface, browser_, view->StartAutocomplete(value.text)));
omnibox_text_field->SetSize(width, 0);
omnibox_text_field->SetHintText(
l10n_util::GetStringUTF16(IDS_SEARCH_OR_TYPE_URL));
omnibox_text_field->SetName(kOmniboxTextField);
omnibox_text_field->set_x_anchoring(LEFT);
omnibox_text_field->SetTranslate(kOmniboxTextMarginDMM, 0, 0);
omnibox_text_field->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) {
return m->omnibox_editing_enabled() &&
m->active_modal_prompt_type == kModalPromptTypeNone;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](TextInput* e, Model* m, const bool& v) {
if (v) {
e->RequestFocus();
} else {
e->RequestUnfocus();
}
},
base::Unretained(omnibox_text_field.get()),
base::Unretained(model_))));
omnibox_text_field->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) { return m->has_mode_in_stack(kModeEditingOmnibox); },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](TextInput* e, Model* m, const bool& unused) {
m->omnibox_text_field_info = TextInputInfo();
},
base::Unretained(omnibox_text_field.get()),
base::Unretained(model_))));
omnibox_text_field->AddBinding(std::make_unique<Binding<AutocompleteStatus>>(
VR_BIND_LAMBDA(
[](Model* m) {
AutocompleteStatus state;
state.active = m->has_mode_in_stack(kModeEditingOmnibox);
state.input = m->omnibox_text_field_info.text;
return state;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiBrowserInterface* browser, const AutocompleteStatus& r) {
if (r.active) {
browser->StartAutocomplete(r.input);
} else {
browser->StopAutocomplete();
}
},
base::Unretained(browser_))));
VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::omnibox_text,
&TextInput::SetTextColor);
VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::cursor,
&TextInput::SetCursorColor);
VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::omnibox_hint,
&TextInput::SetHintColor);
auto mic_icon = Create<VectorIcon>(kNone, kPhaseForeground, 100);
mic_icon->set_hit_testable(false);
mic_icon->SetIcon(vector_icons::kMicIcon);
mic_icon->SetSize(kOmniboxTextFieldIconSizeDMM, kOmniboxTextFieldIconSizeDMM);
VR_BIND_COLOR(model_, mic_icon.get(), &ColorScheme::omnibox_text,
&VectorIcon::SetColor);
auto mic_icon_box = Create<Button>(
kNone, kPhaseForeground,
base::BindRepeating(
[](UiBrowserInterface* b, Ui* ui) { b->SetVoiceSearchActive(true); },
base::Unretained(browser_), base::Unretained(ui_)));
mic_icon_box->set_hover_offset(kOmniboxTextFieldIconButtonHoverOffsetDMM);
mic_icon_box->SetSize(kOmniboxTextFieldIconButtonSizeDMM,
kOmniboxTextFieldIconButtonSizeDMM);
mic_icon_box->set_corner_radius(kOmniboxTextFieldIconButtonRadiusDMM);
VR_BIND_VISIBILITY(
mic_icon_box,
!model->incognito && model->speech.has_or_can_request_audio_permission);
VR_BIND_BUTTON_COLORS(model_, mic_icon_box.get(),
&ColorScheme::omnibox_voice_search_button_colors,
&Button::SetButtonColors);
mic_icon_box->AddChild(std::move(mic_icon));
auto text_field_layout = Create<LinearLayout>(
kOmniboxTextFieldLayout, kPhaseNone, LinearLayout::kRight);
text_field_layout->set_hit_testable(false);
text_field_layout->AddChild(std::move(omnibox_text_field));
text_field_layout->AddChild(std::move(mic_icon_box));
// Set up the vector binding to manage suggestions dynamically.
SuggestionSetBinding::ModelAddedCallback added_callback =
base::BindRepeating(&OnSuggestionModelAdded, base::Unretained(scene_),
base::Unretained(browser_), base::Unretained(ui_),
base::Unretained(model_));
SuggestionSetBinding::ModelRemovedCallback removed_callback =
base::BindRepeating(&OnSuggestionModelRemoved, base::Unretained(scene_));
auto suggestions_outer_layout =
std::make_unique<LinearLayout>(LinearLayout::kDown);
suggestions_outer_layout->SetName(kOmniboxSuggestionsOuterLayout);
suggestions_outer_layout->set_hit_testable(false);
auto suggestions_layout = std::make_unique<LinearLayout>(LinearLayout::kUp);
suggestions_layout->SetName(kOmniboxSuggestions);
suggestions_layout->SetDrawPhase(kPhaseNone);
suggestions_layout->set_hit_testable(false);
suggestions_layout->AddBinding(std::make_unique<SuggestionSetBinding>(
&model_->omnibox_suggestions, added_callback, removed_callback));
auto button_scaler =
std::make_unique<ScaledDepthAdjuster>(kOmniboxCloseButtonDepthOffset);
auto close_button = Create<DiscButton>(
kOmniboxCloseButton, kPhaseForeground,
base::BindRepeating(
[](Model* model) { model->pop_mode(kModeEditingOmnibox); },
base::Unretained(model_)),
vector_icons::kBackArrowIcon);
close_button->SetSize(kOmniboxCloseButtonDiameterDMM,
kOmniboxCloseButtonDiameterDMM);
close_button->SetTranslate(0, kOmniboxCloseButtonVerticalOffsetDMM, 0);
close_button->SetRotate(1, 0, 0, atan(kOmniboxCloseButtonVerticalOffsetDMM));
close_button->set_hover_offset(kButtonZOffsetHoverDMM);
VR_BIND_BUTTON_COLORS(model_, close_button.get(), &ColorScheme::button_colors,
&DiscButton::SetButtonColors);
auto spacer = CreateOmniboxSpacer(model_);
spacer->SetCornerRadii(
{kOmniboxCornerRadiusDMM, kOmniboxCornerRadiusDMM, 0, 0});
suggestions_outer_layout->AddChild(std::move(spacer));
suggestions_outer_layout->AddChild(std::move(suggestions_layout));
suggestions_outer_layout->AddChild(CreateOmniboxSpacer(model_));
omnibox_container->AddChild(std::move(text_field_layout));
omnibox_outer_layout->AddChild(std::move(omnibox_container));
omnibox_outer_layout->AddChild(std::move(omnibox_outer_layout_spacer));
omnibox_outer_layout->AddChild(std::move(suggestions_outer_layout));
shadow->AddChild(std::move(omnibox_outer_layout));
button_scaler->AddChild(std::move(close_button));
omnibox_root->AddChild(std::move(shadow));
omnibox_root->AddChild(std::move(button_scaler));
visibility_toggle_for_audio_permission->AddChild(std::move(omnibox_root));
scaler->AddChild(std::move(visibility_toggle_for_audio_permission));
visibility_control_root->AddChild(std::move(scaler));
scene_->AddUiElement(k2dBrowsingRepositioner,
std::move(visibility_control_root));
}
void UiSceneCreator::CreateCloseButton() {
base::RepeatingCallback<void()> click_handler = base::BindRepeating(
[](Model* model, UiBrowserInterface* browser) {
if (model->fullscreen_enabled()) {
browser->ExitFullscreen();
}
if (model->in_cct) {
browser->ExitCct();
}
},
base::Unretained(model_), base::Unretained(browser_));
std::unique_ptr<DiscButton> element =
Create<DiscButton>(kCloseButton, kPhaseForeground, click_handler,
vector_icons::kClose16Icon);
element->SetSize(kCloseButtonDiameter, kCloseButtonDiameter);
element->set_hover_offset(kButtonZOffsetHoverDMM * kCloseButtonDistance);
element->SetTranslate(0, kCloseButtonVerticalOffset, -kCloseButtonDistance);
VR_BIND_BUTTON_COLORS(model_, element.get(), &ColorScheme::button_colors,
&DiscButton::SetButtonColors);
// Close button is a special control element that needs to be hidden when
// in WebVR, but it needs to be visible when in cct or fullscreen.
VR_BIND_VISIBILITY(element, model->fullscreen_enabled() || model->in_cct);
element->AddBinding(
VR_BIND(bool, Model, model_, model->fullscreen_enabled(), UiElement,
element.get(),
view->SetTranslate(0,
value ? kCloseButtonFullscreenVerticalOffset
: kCloseButtonVerticalOffset,
value ? -kCloseButtonFullscreenDistance
: -kCloseButtonDistance)));
element->AddBinding(VR_BIND(
bool, Model, model_, model->fullscreen_enabled(), UiElement,
element.get(),
view->SetSize(
value ? kCloseButtonFullscreenDiameter : kCloseButtonDiameter,
value ? kCloseButtonFullscreenDiameter : kCloseButtonDiameter)));
scene_->AddUiElement(k2dBrowsingForeground, std::move(element));
}
void UiSceneCreator::CreateExitPrompt() {
std::unique_ptr<UiElement> element;
// Place an invisible but hittable plane behind the exit prompt, to keep the
// reticle roughly planar with the content if near content.
auto backplane = std::make_unique<InvisibleHitTarget>();
backplane->SetDrawPhase(kPhaseForeground);
backplane->SetName(kExitPromptBackplane);
backplane->SetSize(kPromptBackplaneSize, kPromptBackplaneSize);
backplane->SetTranslate(0.0,
kContentVerticalOffset + kExitPromptVerticalOffset,
-kContentDistance);
EventHandlers event_handlers;
event_handlers.button_up = base::BindRepeating(
[](UiBrowserInterface* browser, Model* m) {
browser->OnExitVrPromptResult(
ExitVrPromptChoice::CHOICE_NONE,
GetReasonForPrompt(m->active_modal_prompt_type));
},
base::Unretained(browser_), base::Unretained(model_));
backplane->set_event_handlers(event_handlers);
VR_BIND_VISIBILITY(backplane, model->active_modal_prompt_type ==
kModalPromptTypeExitVRForSiteInfo);
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(backplane));
std::unique_ptr<ExitPrompt> exit_prompt = std::make_unique<ExitPrompt>(
512,
base::BindRepeating(&UiBrowserInterface::OnExitVrPromptResult,
base::Unretained(browser_),
ExitVrPromptChoice::CHOICE_STAY),
base::BindRepeating(&UiBrowserInterface::OnExitVrPromptResult,
base::Unretained(browser_),
ExitVrPromptChoice::CHOICE_EXIT));
exit_prompt->SetName(kExitPrompt);
exit_prompt->SetDrawPhase(kPhaseForeground);
exit_prompt->SetSize(kExitPromptWidth, kExitPromptHeight);
VR_BIND_COLOR(model_, exit_prompt.get(), &ColorScheme::prompt_foreground,
&TexturedElement::SetForegroundColor);
VR_BIND_BUTTON_COLORS(model_, exit_prompt.get(),
&ColorScheme::prompt_primary_button_colors,
&ExitPrompt::SetPrimaryButtonColors);
VR_BIND_BUTTON_COLORS(model_, exit_prompt.get(),
&ColorScheme::prompt_secondary_button_colors,
&ExitPrompt::SetSecondaryButtonColors);
exit_prompt->AddBinding(std::make_unique<Binding<ModalPromptType>>(
VR_BIND_LAMBDA([](Model* m) { return m->active_modal_prompt_type; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](ExitPrompt* e, const ModalPromptType& p) {
e->set_reason(GetReasonForPrompt(p));
switch (p) {
case kModalPromptTypeExitVRForSiteInfo:
e->SetContentMessageId(
IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION_SITE_INFO);
break;
default:
e->SetContentMessageId(IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION);
break;
}
},
base::Unretained(exit_prompt.get()))));
scene_->AddUiElement(kExitPromptBackplane, std::move(exit_prompt));
}
void UiSceneCreator::CreateAudioPermissionPrompt() {
std::unique_ptr<UiElement> element;
// Place an invisible but hittable plane behind the exit prompt, to keep the
// reticle roughly planar with the content if near content.
auto backplane = std::make_unique<InvisibleHitTarget>();
backplane->SetDrawPhase(kPhaseForeground);
backplane->SetName(kAudioPermissionPromptBackplane);
backplane->SetSize(kPromptBackplaneSize, kPromptBackplaneSize);
backplane->SetTranslate(0.0, kContentVerticalOffset, -kOverlayPlaneDistance);
EventHandlers event_handlers;
event_handlers.button_up = base::BindRepeating(
[](UiBrowserInterface* browser, Model* m) {
browser->OnExitVrPromptResult(
ExitVrPromptChoice::CHOICE_NONE,
GetReasonForPrompt(m->active_modal_prompt_type));
},
base::Unretained(browser_), base::Unretained(model_));
backplane->set_event_handlers(event_handlers);
backplane->SetVisible(false);
backplane->SetTransitionedProperties({OPACITY});
VR_BIND_VISIBILITY(
backplane,
model->active_modal_prompt_type ==
kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission);
std::unique_ptr<Shadow> shadow = std::make_unique<Shadow>();
shadow->SetName(kAudioPermissionPromptShadow);
shadow->SetDrawPhase(kPhaseForeground);
std::unique_ptr<AudioPermissionPrompt> prompt =
std::make_unique<AudioPermissionPrompt>(
1024,
base::BindRepeating(
&UiBrowserInterface::OnExitVrPromptResult,
base::Unretained(browser_), ExitVrPromptChoice::CHOICE_EXIT,
UiUnsupportedMode::kVoiceSearchNeedsRecordAudioOsPermission),
base::BindRepeating(
&UiBrowserInterface::OnExitVrPromptResult,
base::Unretained(browser_), ExitVrPromptChoice::CHOICE_STAY,
UiUnsupportedMode::kVoiceSearchNeedsRecordAudioOsPermission));
prompt->SetName(kAudioPermissionPrompt);
prompt->SetDrawPhase(kPhaseForeground);
prompt->SetSize(kAudioPermissionPromptWidth, kAudioPermissionPromptHeight);
prompt->SetTranslate(0.0, 0.0f, kAudionPermisionPromptDepth);
VR_BIND_BUTTON_COLORS(
model_, prompt.get(),
&ColorScheme::audio_permission_prompt_primary_button_colors,
&AudioPermissionPrompt::SetPrimaryButtonColors);
VR_BIND_BUTTON_COLORS(
model_, prompt.get(),
&ColorScheme::audio_permission_prompt_secondary_button_colors,
&AudioPermissionPrompt::SetSecondaryButtonColors);
VR_BIND_COLOR(model_, prompt.get(),
&ColorScheme::audio_permission_prompt_icon_foreground,
&AudioPermissionPrompt::SetIconColor);
VR_BIND_COLOR(model_, prompt.get(),
&ColorScheme::audio_permission_prompt_background,
&TexturedElement::SetBackgroundColor);
VR_BIND_COLOR(model_, prompt.get(), &ColorScheme::element_foreground,
&TexturedElement::SetForegroundColor);
shadow->AddChild(std::move(prompt));
backplane->AddChild(std::move(shadow));
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(backplane));
}
void UiSceneCreator::CreateWebVrOverlayElements() {
// Create url toast shown when WebVR is auto-presented.
auto parent = CreateTransientParent(kWebVrUrlToastTransientParent,
kWebVrUrlToastTimeoutSeconds, true);
parent->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* model, UiElement* splash_screen) {
// The url toast should only be visible when the splash screen is
// not visible.
return model->web_vr_autopresentation_enabled() &&
model->web_vr.has_produced_frames() &&
splash_screen->GetTargetOpacity() == 0.f;
},
base::Unretained(model_),
base::Unretained(
scene_->GetUiElementByName(kSplashScreenTransientParent))),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& value) { e->SetVisible(value); },
base::Unretained(parent.get()))));
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(parent));
auto url_toast = std::make_unique<WebVrUrlToast>(
512, base::BindRepeating(&UiBrowserInterface::OnUnsupportedMode,
base::Unretained(browser_)));
url_toast->SetName(kWebVrUrlToast);
url_toast->set_opacity_when_visible(kWebVrUrlToastOpacity);
url_toast->SetDrawPhase(kPhaseOverlayForeground);
url_toast->set_hit_testable(false);
url_toast->SetTranslate(
0, kWebVrToastDistance * sin(kWebVrUrlToastRotationRad),
-kWebVrToastDistance * cos(kWebVrUrlToastRotationRad));
url_toast->SetRotate(1, 0, 0, kWebVrUrlToastRotationRad);
url_toast->SetSize(kWebVrUrlToastWidth, kWebVrUrlToastHeight);
VR_BIND_COLOR(model_, url_toast.get(),
&ColorScheme::web_vr_transient_toast_background,
&TexturedElement::SetBackgroundColor);
VR_BIND_COLOR(model_, url_toast.get(),
&ColorScheme::web_vr_transient_toast_foreground,
&TexturedElement::SetForegroundColor);
url_toast->AddBinding(VR_BIND_FUNC(ToolbarState, Model, model_,
model->toolbar_state, WebVrUrlToast,
url_toast.get(), SetToolbarState));
scene_->AddUiElement(kWebVrUrlToastTransientParent, std::move(url_toast));
// Create "Press app button to exit" toast.
parent =
CreateTransientParent(kExclusiveScreenToastViewportAwareTransientParent,
kToastTimeoutSeconds, false);
// When we first get a web vr frame, we switch states to
// kWebVrNoTimeoutPending, when that happens, we want to SetVisible(true) to
// kick the visibility of this element.
VR_BIND_VISIBILITY(parent, model->web_vr.has_produced_frames() &&
model->web_vr.show_exit_toast);
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(parent));
auto scaler = std::make_unique<ScaledDepthAdjuster>(kWebVrToastDistance);
auto exit_toast = std::make_unique<Toast>();
exit_toast->SetName(kExclusiveScreenToastViewportAware);
exit_toast->SetDrawPhase(kPhaseOverlayForeground);
exit_toast->set_hit_testable(false);
exit_toast->SetTranslate(0, sin(kWebVrAngleRadians),
1.0 - cos(kWebVrAngleRadians));
exit_toast->SetRotate(1, 0, 0, kWebVrAngleRadians);
exit_toast->set_padding(kExclusiveScreenToastXPaddingDMM,
kExclusiveScreenToastYPaddingDMM);
exit_toast->set_corner_radius(kExclusiveScreenToastCornerRadiusDMM);
exit_toast->AddText(l10n_util::GetStringUTF16(IDS_PRESS_APP_TO_EXIT),
kExclusiveScreenToastTextFontHeightDMM,
TextLayoutMode::kSingleLineFixedHeight);
VR_BIND_COLOR(model_, exit_toast.get(),
&ColorScheme::exclusive_screen_toast_background,
&Toast::SetBackgroundColor);
VR_BIND_COLOR(model_, exit_toast.get(),
&ColorScheme::exclusive_screen_toast_foreground,
&Toast::SetForegroundColor);
scaler->AddChild(std::move(exit_toast));
scene_->AddUiElement(kExclusiveScreenToastViewportAwareTransientParent,
std::move(scaler));
}
void UiSceneCreator::CreateFullscreenToast() {
auto parent = CreateTransientParent(kExclusiveScreenToastTransientParent,
kToastTimeoutSeconds, false);
VR_BIND_VISIBILITY(parent, model->fullscreen_enabled());
scene_->AddUiElement(k2dBrowsingForeground, std::move(parent));
auto scaler = std::make_unique<ScaledDepthAdjuster>(kFullscreenDistance);
auto element = std::make_unique<Toast>();
element->SetName(kExclusiveScreenToast);
element->SetDrawPhase(kPhaseForeground);
element->set_hit_testable(false);
element->SetTranslate(0, kFullScreenToastOffsetDMM, 0);
element->set_padding(kExclusiveScreenToastXPaddingDMM,
kExclusiveScreenToastYPaddingDMM);
element->set_corner_radius(kExclusiveScreenToastCornerRadiusDMM);
element->AddText(l10n_util::GetStringUTF16(IDS_PRESS_APP_TO_EXIT),
kExclusiveScreenToastTextFontHeightDMM,
TextLayoutMode::kSingleLineFixedHeight);
VR_BIND_COLOR(model_, element.get(),
&ColorScheme::exclusive_screen_toast_background,
&Toast::SetBackgroundColor);
VR_BIND_COLOR(model_, element.get(),
&ColorScheme::exclusive_screen_toast_foreground,
&Toast::SetForegroundColor);
scaler->AddChild(std::move(element));
scene_->AddUiElement(kExclusiveScreenToastTransientParent, std::move(scaler));
}
} // namespace vr