blob: 1e9a9c1e38f4ffcfd72ad71f6a737e1eeaf6986a [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/bind_helpers.h"
#include "base/callback.h"
#include "base/i18n/case_conversion.h"
#include "base/numerics/math_constants.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "cc/animation/animation_curve.h"
#include "cc/animation/animation_target.h"
#include "cc/animation/keyframe_effect.h"
#include "cc/animation/keyframed_animation_curve.h"
#include "chrome/browser/vr/content_input_delegate.h"
#include "chrome/browser/vr/databinding/binding.h"
#include "chrome/browser/vr/databinding/vector_binding.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/full_screen_rect.h"
#include "chrome/browser/vr/elements/indicator_spec.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/omnibox_text_field.h"
#include "chrome/browser/vr/elements/oval.h"
#include "chrome/browser/vr/elements/paged_grid_layout.h"
#include "chrome/browser/vr/elements/paged_scroll_view.h"
#include "chrome/browser/vr/elements/platform_ui_element.h"
#include "chrome/browser/vr/elements/rect.h"
#include "chrome/browser/vr/elements/repositioner.h"
#include "chrome/browser/vr/elements/resizer.h"
#include "chrome/browser/vr/elements/reticle.h"
#include "chrome/browser/vr/elements/scaled_depth_adjuster.h"
#include "chrome/browser/vr/elements/scrollable_element.h"
#include "chrome/browser/vr/elements/spinner.h"
#include "chrome/browser/vr/elements/text.h"
#include "chrome/browser/vr/elements/text_button.h"
#include "chrome/browser/vr/elements/text_input.h"
#include "chrome/browser/vr/elements/throbber.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_text.h"
#include "chrome/browser/vr/elements/vector_icon.h"
#include "chrome/browser/vr/elements/viewport_aware_root.h"
#include "chrome/browser/vr/keyboard_delegate.h"
#include "chrome/browser/vr/model/model.h"
#include "chrome/browser/vr/model/platform_toast.h"
#include "chrome/browser/vr/model/tab_model.h"
#include "chrome/browser/vr/platform_ui_input_delegate.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/strings/grit/components_strings.h"
#include "components/toolbar/vector_icons.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),
#ifndef NDEBUG
color_string,
#endif
base::BindRepeating(
[](V* v, S s, const SkColor& value) { (v->*s)(value); },
base::Unretained(view), setter)
#ifndef NDEBUG
,
setter_string
#endif
));
}
#define VR_BIND_COLOR(m, v, c, s) BindColor(m, v, c, #c, s, #s)
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),
#ifndef NDEBUG
colors_string,
#endif
base::BindRepeating(
[](V* v, S s, const ButtonColors& value) { (v->*s)(value); },
base::Unretained(view), setter)
#ifndef NDEBUG
,
setter_string
#endif
));
}
#define VR_BIND_BUTTON_COLORS(m, v, c, s) BindButtonColors(m, v, c, #c, s, #s)
#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,
AudioDelegate* audio_delegate,
SuggestionBinding* element_binding) {
auto icon = std::make_unique<VectorIcon>(100);
icon->SetDrawPhase(kPhaseForeground);
icon->SetType(kTypeOmniboxSuggestionIcon);
icon->SetSize(kSuggestionIconSizeDMM, kSuggestionIconSizeDMM);
icon->AddBinding(VR_BIND_FUNC(SkColor, Model, model,
model->color_scheme().url_bar_button.foreground,
VectorIcon, icon.get(), SetColor));
VectorIcon* p_icon = icon.get();
auto icon_box = std::make_unique<UiElement>();
icon_box->SetDrawPhase(kPhaseNone);
icon_box->SetType(kTypeOmniboxSuggestionIconField);
icon_box->set_hit_testable(true);
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->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
content_text->SetFieldWidth(kSuggestionTextFieldWidthDMM);
content_text->SetAlignment(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->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
description_text->SetFieldWidth(kSuggestionTextFieldWidthDMM);
description_text->SetAlignment(kTextAlignmentLeft);
Text* p_description_text = description_text.get();
auto text_layout = std::make_unique<LinearLayout>(LinearLayout::kDown);
text_layout->SetType(kTypeOmniboxSuggestionTextLayout);
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->set_hit_testable(true);
right_margin->SetSize(kSuggestionRightMarginDMM, kSuggestionHeightDMM);
auto suggestion_layout = std::make_unique<LinearLayout>(LinearLayout::kRight);
suggestion_layout->SetType(kTypeOmniboxSuggestionLayout);
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,
NavigationMethod::kOmniboxSuggestionSelected);
ui->OnUiRequestedNavigation();
},
base::Unretained(browser), base::Unretained(ui),
base::Unretained(model), base::Unretained(element_binding)),
audio_delegate);
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::url_bar_button,
&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->SetVisible(!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, /*is_bookmark=*/false, /*is_tab_match=*/false))));
element_binding->set_view(background.get());
scene->AddUiElement(kOmniboxSuggestions, std::move(background));
}
void OnSuggestionModelRemoved(UiScene* scene, SuggestionBinding* binding) {
scene->RemoveUiElement(binding->view()->id());
}
typedef VectorBinding<TabModel, UiElement> TabSetBinding;
typedef typename TabSetBinding::ElementBinding TabBinding;
void OnTabModelAdded(UiScene* scene,
Model* model,
bool incognito,
PagedGridLayout* tabs_view,
AudioDelegate* audio_delegate,
UiBrowserInterface* browser,
TabBinding* element_binding) {
auto item = Create<Button>(kNone, kPhaseForeground, base::DoNothing(),
audio_delegate);
item->SetButtonColors(
ColorScheme::GetColorScheme(incognito ? ColorScheme::kModeIncognito
: ColorScheme::kModeNormal)
.disc_button_colors);
item->SetSize(kTabItemWidthDMM, kTabItemHeightDMM);
item->SetTransitionedProperties({OPACITY});
item->set_corner_radius(kTabItemCornerRadiusDMM);
item->set_hover_offset(0.0f);
item->SetType(kTypeTabItem);
item->AddBinding(std::make_unique<Binding<PagedGridLayout::PageState>>(
VR_BIND_LAMBDA(
[](PagedGridLayout* paged_layout, UiElement* item) {
return paged_layout->GetPageState(item);
},
base::Unretained(tabs_view), base::Unretained(item.get())),
VR_BIND_LAMBDA(
[](Button* item, const PagedGridLayout::PageState& page_state) {
switch (page_state) {
case PagedGridLayout::kActive:
item->SetOpacity(kTabsViewActivePageOpacity);
item->SetEnabled(true);
break;
case PagedGridLayout::kInactive:
item->SetOpacity(kTabsViewInactivePageOpacity);
item->SetEnabled(false);
break;
default:
item->SetOpacity(kTabsViewHiddenPageOpacity);
item->SetEnabled(false);
break;
}
},
base::Unretained(item.get()))));
element_binding->bindings().push_back(std::make_unique<Binding<int>>(
VR_BIND_LAMBDA(
[](TabBinding* element_binding) {
return element_binding->model()->id;
},
base::Unretained(element_binding)),
VR_BIND_LAMBDA(
[](TabBinding* element_binding, UiBrowserInterface* browser,
Model* model, bool incognito, const int& id) {
static_cast<Button*>(
element_binding->view()->GetDescendantByType(kTypeTabItem))
->set_click_handler(base::BindRepeating(
[](UiBrowserInterface* browser, Model* model, int id,
bool incognito) {
browser->SelectTab(id, incognito);
model->pop_mode(kModeTabsView);
},
base::Unretained(browser), base::Unretained(model), id,
incognito));
},
base::Unretained(element_binding), base::Unretained(browser),
base::Unretained(model), incognito)));
// TODO(crbug.com/838937): This is just a placeholder text. Replace with
// proper tab item.
auto text = Create<Text>(kNone, kPhaseForeground, kTabItemTextSizeDMM);
text->SetLayoutMode(TextLayoutMode::kSingleLine);
element_binding->bindings().push_back(VR_BIND_FUNC(
base::string16, TabBinding, element_binding,
model->model()->title.empty() ? base::string16()
: model->model()->title.substr(0, 1),
Text, text.get(), SetText));
text->SetColor(ColorScheme::GetColorScheme(incognito
? ColorScheme::kModeIncognito
: ColorScheme::kModeNormal)
.tab_item_text);
item->background()->AddChild(std::move(text));
auto shadow = Create<Shadow>(kNone, kPhaseForeground);
shadow->set_contributes_to_parent_bounds(false);
shadow->set_x_anchoring(RIGHT);
shadow->set_y_anchoring(TOP);
shadow->set_intensity(kTabsViewRemoveButtonShadowIntensity);
shadow->SetTransitionedProperties({OPACITY});
auto remove_button =
Create<DiscButton>(kNone, kPhaseForeground, base::DoNothing(),
vector_icons::kCloseRoundedIcon, nullptr);
remove_button->SetTranslate(0, 0, kTabsViewRemoveButtonDepthOffsetDMM);
remove_button->SetSize(kTabsViewRemoveButtonSizeDMM,
kTabsViewRemoveButtonSizeDMM);
remove_button->SetType(kTypeTabItemRemoveButton);
remove_button->set_hover_offset(kTabsViewRemoveButtonHoverOffsetDMM);
Sounds sounds;
sounds.hover_enter = kSoundButtonHover;
sounds.button_down = kSoundButtonClick;
remove_button->SetSounds(sounds, audio_delegate);
VR_BIND_BUTTON_COLORS(model, remove_button.get(),
&ColorScheme::disc_button_colors,
&DiscButton::SetButtonColors);
element_binding->bindings().push_back(std::make_unique<Binding<int>>(
VR_BIND_LAMBDA(
[](TabBinding* element_binding) {
return element_binding->model()->id;
},
base::Unretained(element_binding)),
VR_BIND_LAMBDA(
[](TabBinding* element_binding, UiBrowserInterface* browser,
bool incognito, const int& id) {
static_cast<Button*>(element_binding->view()->GetDescendantByType(
kTypeTabItemRemoveButton))
->set_click_handler(base::BindRepeating(
[](UiBrowserInterface* browser, int id, bool incognito) {
browser->CloseTab(id, incognito);
},
base::Unretained(browser), id, incognito));
},
base::Unretained(element_binding), base::Unretained(browser),
incognito)));
shadow->set_shadow_caster(remove_button->background());
shadow->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Button* item, Button* remove_button) {
return item->hovered() || remove_button->hovered();
},
base::Unretained(item.get()), base::Unretained(remove_button.get())),
VR_BIND_LAMBDA([](UiElement* shadow,
const bool& hovered) { shadow->SetVisible(hovered); },
base::Unretained(shadow.get()))));
shadow->AddChild(std::move(remove_button));
item->background()->AddChild(std::move(shadow));
element_binding->set_view(item.get());
scene->AddUiElement(tabs_view, std::move(item));
}
void OnTabModelRemoved(UiScene* scene, TabBinding* 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);
if (animate_opacity)
element->SetTransitionedProperties({OPACITY});
return element;
}
std::unique_ptr<UiElement> CreateSpacer(float width, float height) {
auto spacer = Create<UiElement>(kNone, kPhaseNone);
spacer->SetType(kTypeSpacer);
spacer->SetSize(width, height);
return spacer;
}
std::unique_ptr<UiElement> CreatePrompt(Model* model) {
auto primary_button = Create<TextButton>(kNone, kPhaseForeground,
kPromptButtonTextSize, nullptr);
primary_button->SetType(kTypePromptPrimaryButton);
primary_button->set_corner_radius(kPromptButtonCornerRadius);
VR_BIND_BUTTON_COLORS(model, primary_button.get(),
&ColorScheme::modal_prompt_primary_button_colors,
&Button::SetButtonColors);
auto secondary_button = Create<TextButton>(kNone, kPhaseForeground,
kPromptButtonTextSize, nullptr);
secondary_button->SetType(kTypePromptSecondaryButton);
secondary_button->set_corner_radius(kPromptButtonCornerRadius);
VR_BIND_BUTTON_COLORS(model, secondary_button.get(),
&ColorScheme::modal_prompt_secondary_button_colors,
&Button::SetButtonColors);
auto button_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kLeft);
button_layout->AddChild(std::move(primary_button));
button_layout->AddChild(std::move(secondary_button));
button_layout->set_margin(kPromptButtonGap);
button_layout->set_x_anchoring(RIGHT);
auto icon = Create<VectorIcon>(kNone, kPhaseForeground, 100);
icon->SetType(kTypePromptIcon);
icon->SetSize(kPromptIconSize, kPromptIconSize);
icon->set_y_anchoring(TOP);
VR_BIND_COLOR(model, icon.get(), &ColorScheme::modal_prompt_icon_foreground,
&VectorIcon::SetColor);
auto text = Create<Text>(kNone, kPhaseForeground, kPromptFontSize);
text->SetType(kTypePromptText);
text->SetLayoutMode(kMultiLineFixedWidth);
text->SetAlignment(kTextAlignmentLeft);
text->SetFieldWidth(kPromptTextWidth);
VR_BIND_COLOR(model, text.get(), &ColorScheme::modal_prompt_foreground,
&Text::SetColor);
// This spacer's padding ensures that the top line of text is aligned with the
// icon even in the multi-line case.
auto text_spacer = CreateSpacer(0, 0);
text_spacer->set_bounds_contain_children(true);
text_spacer->set_padding(0, (kPromptIconSize - kPromptFontSize) / 2, 0, 0);
text_spacer->set_y_anchoring(TOP);
text_spacer->AddChild(std::move(text));
auto message_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
message_layout->set_margin(kPromptIconTextGap);
message_layout->AddChild(std::move(icon));
message_layout->AddChild(std::move(text_spacer));
auto prompt_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kDown);
prompt_layout->set_margin(kPromptMessageButtonGap);
prompt_layout->AddChild(std::move(message_layout));
prompt_layout->AddChild(std::move(button_layout));
auto background = Create<Rect>(kNone, kPhaseForeground);
background->SetType(kTypePromptBackground);
background->set_bounds_contain_children(true);
background->set_hit_testable(true);
background->set_padding(kPromptPadding, kPromptPadding);
background->SetTranslate(0, 0, kPromptShadowOffsetDMM);
background->set_corner_radius(kPromptCornerRadius);
background->AddChild(std::move(prompt_layout));
VR_BIND_COLOR(model, background.get(), &ColorScheme::modal_prompt_background,
&Rect::SetColor);
auto shadow = Create<Shadow>(kNone, kPhaseForeground);
shadow->SetType(kTypePromptShadow);
shadow->AddChild(std::move(background));
// Place an invisible but hittable plane behind the exit prompt, to keep the
// reticle roughly planar with the content if near content.
auto backplane = Create<InvisibleHitTarget>(kNone, kPhaseForeground);
backplane->SetType(kTypePromptBackplane);
backplane->SetTranslate(0, kPromptVerticalOffsetDMM, 0);
backplane->SetSize(kBackplaneSize, kBackplaneSize);
backplane->SetTransitionedProperties({OPACITY});
backplane->AddChild(std::move(shadow));
auto scaler = Create<ScaledDepthAdjuster>(kNone, kPhaseNone, kPromptDistance);
scaler->SetType(kTypeScaledDepthAdjuster);
scaler->AddChild(std::move(backplane));
scaler->set_contributes_to_parent_bounds(false);
return scaler;
}
base::RepeatingCallback<void()> CreatePromptCallback(
UiUnsupportedMode mode,
ExitVrPromptChoice choice,
Model* model,
UiBrowserInterface* browser) {
return base::BindRepeating(
[](UiUnsupportedMode mode, ExitVrPromptChoice choice, Model* m,
UiBrowserInterface* b) {
b->OnExitVrPromptResult(choice, mode);
m->active_modal_prompt_type = kModalPromptTypeNone;
m->pop_mode(kModeModalPrompt);
},
mode, choice, base::Unretained(model), base::Unretained(browser));
}
std::unique_ptr<UiElement> CreateControllerLabel(UiElementName name,
float z_offset,
const base::string16& text,
Model* model) {
auto layout = Create<LinearLayout>(name, kPhaseNone, LinearLayout::kLeft);
layout->set_margin(kControllerLabelLayoutMargin);
layout->SetTranslate(0, 0, z_offset);
layout->set_contributes_to_parent_bounds(false);
layout->AddBinding(VR_BIND_FUNC(
LayoutAlignment, Model, model,
model->controller.handedness == PlatformController::kRightHanded ? LEFT
: RIGHT,
LinearLayout, layout.get(), set_x_centering));
layout->AddBinding(VR_BIND_FUNC(
LinearLayout::Direction, Model, model,
model->controller.handedness == PlatformController::kRightHanded
? LinearLayout::kRight
: LinearLayout::kLeft,
LinearLayout, layout.get(), set_direction));
auto spacer = std::make_unique<UiElement>();
spacer->SetType(kTypeSpacer);
spacer->SetVisible(true);
spacer->set_requires_layout(true);
spacer->SetSize(kControllerLabelSpacerSize, kControllerLabelSpacerSize);
auto callout = Create<Rect>(kNone, kPhaseForeground);
callout->SetVisible(true);
callout->SetColor(SK_ColorWHITE);
callout->SetSize(kControllerLabelCalloutWidth, kControllerLabelCalloutHeight);
callout->SetRotate(1, 0, 0, -base::kPiFloat / 2);
auto label =
Create<Text>(kNone, kPhaseForeground, kControllerLabelFontHeight);
label->SetText(text);
label->SetColor(model->color_scheme().controller_label_callout);
label->SetVisible(true);
label->SetAlignment(kTextAlignmentRight);
label->SetLayoutMode(kSingleLine);
label->SetRotate(1, 0, 0, -base::kPiFloat / 2);
label->SetShadowsEnabled(true);
label->SetScale(kControllerLabelScale, kControllerLabelScale,
kControllerLabelScale);
layout->AddChild(std::move(spacer));
layout->AddChild(std::move(callout));
layout->AddChild(std::move(label));
return layout;
}
std::unique_ptr<UiElement> CreateControllerElement(Model* model) {
auto controller = Create<Controller>(kController, kPhaseForeground);
controller->AddBinding(VR_BIND_FUNC(gfx::Transform, Model, model,
model->controller.transform, Controller,
controller.get(), set_local_transform));
controller->AddBinding(VR_BIND_FUNC(float, Model, model,
model->controller.opacity, Controller,
controller.get(), SetOpacity));
auto touchpad_button =
Create<Rect>(kControllerTouchpadButton, kPhaseForeground);
touchpad_button->SetColor(model->color_scheme().controller_button);
touchpad_button->SetSize(kControllerWidth, kControllerWidth);
touchpad_button->SetRotate(1, 0, 0, -base::kPiFloat / 2);
touchpad_button->SetTranslate(0.0f, 0.0f,
-(kControllerLength - kControllerWidth) / 2);
touchpad_button->SetCornerRadii({kControllerWidth / 2, kControllerWidth / 2,
kControllerWidth / 2, kControllerWidth / 2});
touchpad_button->AddBinding(VR_BIND_FUNC(
SkColor, Model, model,
model->controller.touchpad_button_state == UiInputManager::DOWN
? model->color_scheme().controller_button_down
: model->color_scheme().controller_button,
Rect, touchpad_button.get(), SetColor));
controller->AddChild(std::move(touchpad_button));
auto app_button =
Create<VectorIcon>(kControllerAppButton, kPhaseForeground, 100);
app_button->SetIcon(kDaydreamControllerAppButtonIcon);
app_button->SetColor(model->color_scheme().controller_button);
app_button->SetSize(kControllerSmallButtonSize, kControllerSmallButtonSize);
app_button->SetRotate(1, 0, 0, -base::kPiFloat / 2);
app_button->SetTranslate(0.0f, 0.0f, kControllerAppButtonZ);
app_button->AddBinding(
VR_BIND_FUNC(SkColor, Model, model,
model->controller.app_button_state == UiInputManager::DOWN
? model->color_scheme().controller_button_down
: model->color_scheme().controller_button,
VectorIcon, app_button.get(), SetColor));
controller->AddChild(std::move(app_button));
auto home_button =
Create<VectorIcon>(kControllerHomeButton, kPhaseForeground, 100);
home_button->SetIcon(kDaydreamControllerHomeButtonIcon);
home_button->SetColor(model->color_scheme().controller_button);
home_button->SetSize(kControllerSmallButtonSize, kControllerSmallButtonSize);
home_button->SetRotate(1, 0, 0, -base::kPiFloat / 2);
home_button->SetTranslate(0.0f, 0.0f, kControllerHomeButtonZ);
home_button->AddBinding(
VR_BIND_FUNC(SkColor, Model, model,
model->controller.home_button_state == UiInputManager::DOWN
? model->color_scheme().controller_button_down
: model->color_scheme().controller_button,
VectorIcon, home_button.get(), SetColor));
controller->AddChild(std::move(home_button));
auto battery_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
battery_layout->set_margin(kControllerBatteryDotMargin);
battery_layout->SetRotate(1, 0, 0, -base::kPiFloat / 2);
battery_layout->SetTranslate(0.0f, 0.0f, kControllerBatteryDotZ);
for (int i = 0; i < kControllerBatteryDotCount; ++i) {
auto battery_dot =
Create<Rect>(static_cast<UiElementName>(kControllerBatteryDot0 + i),
kPhaseForeground);
battery_dot->SetSize(kControllerBatteryDotSize, kControllerBatteryDotSize);
battery_dot->set_corner_radius(kControllerBatteryDotSize / 2);
battery_dot->AddBinding(std::make_unique<Binding<SkColor>>(
VR_BIND_LAMBDA(
[](Model* model, int index) {
return model->controller.battery_level > index
? model->color_scheme().controller_battery_full
: model->color_scheme().controller_battery_empty;
},
base::Unretained(model), i),
VR_BIND_LAMBDA(
[](Rect* battery_dot, const SkColor& value) {
battery_dot->SetColor(value);
},
base::Unretained(battery_dot.get()))));
battery_layout->AddChild(std::move(battery_dot));
}
controller->AddChild(std::move(battery_layout));
return controller;
}
EventHandlers CreateRepositioningHandlers(Model* model, UiScene* scene) {
EventHandlers handlers;
handlers.button_down = base::BindRepeating(
[](Model* model) { model->push_mode(kModeRepositionWindow); },
base::Unretained(model));
handlers.button_up = base::BindRepeating(
[](Model* model, Repositioner* repositioner) {
if (repositioner->HasMovedBeyondThreshold())
model->pop_mode(kModeRepositionWindow);
},
base::Unretained(model),
base::Unretained(static_cast<Repositioner*>(
scene->GetUiElementByName(k2dBrowsingRepositioner))));
return handlers;
}
void BindIndicatorText(Model* model, Text* text, const IndicatorSpec& spec) {
text->AddBinding(std::make_unique<Binding<std::pair<bool, bool>>>(
VR_BIND_LAMBDA(
[](Model* model, bool CapturingStateModel::*signal) {
return std::make_pair(model->active_capturing.*signal,
model->background_capturing.*signal);
},
base::Unretained(model), spec.signal),
VR_BIND_LAMBDA(
[](Text* view, int resource, int background_resource,
int potential_resource, const std::pair<bool, bool>& value) {
if (value.first)
view->SetText(l10n_util::GetStringUTF16(resource));
else if (value.second)
view->SetText(l10n_util::GetStringUTF16(background_resource));
else
view->SetText(l10n_util::GetStringUTF16(potential_resource));
},
base::Unretained(text), spec.resource_string,
spec.background_resource_string, spec.potential_resource_string)));
}
std::unique_ptr<UiElement> CreateWebVrIndicator(Model* model,
UiBrowserInterface* browser,
IndicatorSpec spec) {
auto container = Create<Rect>(spec.webvr_name, kPhaseOverlayForeground);
VR_BIND_COLOR(model, container.get(),
&ColorScheme::webvr_permission_background, &Rect::SetColor);
container->set_corner_radius(kWebVrPermissionCornerRadius);
container->set_bounds_contain_children(true);
container->SetVisible(false);
container->set_padding(
kWebVrPermissionLeftPadding, kWebVrPermissionTopPadding,
kWebVrPermissionRightPadding, kWebVrPermissionBottomPadding);
auto layout = Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
layout->set_margin(kWebVrPermissionMargin);
auto icon_element = Create<VectorIcon>(kNone, kPhaseOverlayForeground, 128);
VR_BIND_COLOR(model, icon_element.get(),
&ColorScheme::webvr_permission_foreground,
&VectorIcon::SetColor);
icon_element->set_y_anchoring(TOP);
icon_element->SetSize(kWebVrPermissionIconSize, kWebVrPermissionIconSize);
if (spec.is_url) {
icon_element->AddBinding(VR_BIND_FUNC(
const gfx::VectorIcon*, Model, model, model->toolbar_state.vector_icon,
VectorIcon, icon_element.get(), SetIcon));
} else {
icon_element->SetIcon(spec.icon);
}
std::unique_ptr<UiElement> description_element;
if (spec.is_url) {
auto url_text = Create<UrlText>(
kNone, kPhaseOverlayForeground, kWebVrPermissionFontHeight,
base::BindRepeating(&UiBrowserInterface::OnUnsupportedMode,
base::Unretained(browser),
UiUnsupportedMode::kUnhandledCodePoint)
);
url_text->SetFieldWidth(kWebVrPermissionTextWidth);
url_text->AddBinding(VR_BIND_FUNC(GURL, Model, model,
model->toolbar_state.gurl, UrlText,
url_text.get(), SetUrl));
VR_BIND_COLOR(model, url_text.get(),
&ColorScheme::webvr_permission_foreground,
&UrlText::SetEmphasizedColor);
VR_BIND_COLOR(model, url_text.get(),
&ColorScheme::webvr_permission_foreground,
&UrlText::SetDeemphasizedColor);
description_element = std::move(url_text);
} else {
auto text_element = Create<Text>(kNone, kPhaseOverlayForeground,
kWebVrPermissionFontHeight);
text_element->SetLayoutMode(kMultiLineFixedWidth);
text_element->SetAlignment(kTextAlignmentLeft);
text_element->SetColor(SK_ColorWHITE);
text_element->SetFieldWidth(kWebVrPermissionTextWidth);
if (spec.signal)
BindIndicatorText(model, text_element.get(), spec);
else
text_element->SetText(l10n_util::GetStringUTF16(spec.resource_string));
VR_BIND_COLOR(model, text_element.get(),
&ColorScheme::webvr_permission_foreground, &Text::SetColor);
description_element = std::move(text_element);
}
layout->AddChild(std::move(icon_element));
layout->AddChild(std::move(description_element));
container->AddChild(std::move(layout));
return container;
}
std::unique_ptr<UiElement> CreateHostedUi(
Model* model,
UiBrowserInterface* browser,
UiElementName name,
UiElementName element_name,
float distance) {
auto hosted_ui = Create<PlatformUiElement>(element_name, kPhaseForeground);
hosted_ui->SetSize(kContentWidth * kHostedUiWidthRatio,
kContentHeight * kHostedUiHeightRatio);
// The hosted UI doesn't steal focus so that clikcing on an autofill
// suggestion doesn't hide the keyboard. We will probably need to change this
// when we support the keyboard on native UI elements.
hosted_ui->set_focusable(false);
hosted_ui->set_requires_layout(false);
hosted_ui->set_corner_radius(kContentCornerRadius);
hosted_ui->SetTranslate(0, 0, kHostedUiShadowOffset);
hosted_ui->AddBinding(VR_BIND_FUNC(PlatformUiInputDelegatePtr, Model, model,
model->hosted_platform_ui.delegate,
PlatformUiElement, hosted_ui.get(),
SetDelegate));
hosted_ui->AddBinding(VR_BIND_FUNC(
unsigned int, Model, model, model->hosted_platform_ui.texture_id,
PlatformUiElement, hosted_ui.get(), SetTextureId));
hosted_ui->AddBinding(VR_BIND_FUNC(
UiElementRenderer::TextureLocation, Model, model, model->content_location,
PlatformUiElement, hosted_ui.get(), SetTextureLocation));
hosted_ui->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) { return m->hosted_platform_ui.hosted_ui_enabled; },
base::Unretained(model)),
VR_BIND_LAMBDA(
[](PlatformUiElement* dialog, const bool& enabled) {
dialog->set_requires_layout(enabled);
dialog->set_hit_testable(enabled);
},
base::Unretained(hosted_ui.get()))));
hosted_ui->AddBinding(
VR_BIND(bool, Model, model, model->hosted_platform_ui.floating, UiElement,
hosted_ui.get(),
view->SetTranslate(0, 0, value ? 0 : kHostedUiShadowOffset)));
hosted_ui->AddBinding(std::make_unique<Binding<std::pair<bool, gfx::SizeF>>>(
VR_BIND_LAMBDA(
[](Model* m) {
return std::pair<bool, gfx::SizeF>(
m->hosted_platform_ui.floating,
gfx::SizeF(m->hosted_platform_ui.rect.width(),
m->hosted_platform_ui.rect.height()));
},
base::Unretained(model)),
VR_BIND_LAMBDA(
[](PlatformUiElement* dialog,
const std::pair<bool, gfx::SizeF>& value) {
if (!value.first && value.second.width() > 0) {
float ratio = static_cast<float>(value.second.height()) /
value.second.width();
dialog->SetSize(kContentWidth * kHostedUiWidthRatio,
kContentWidth * kHostedUiWidthRatio * ratio);
} else if (value.first) {
dialog->SetSize(kContentWidth * value.second.width(),
kContentWidth * value.second.height());
}
},
base::Unretained(hosted_ui.get()))));
auto shadow = Create<Shadow>(kNone, kPhaseForeground);
shadow->SetType(kTypePromptShadow);
shadow->SetTranslate(0, 0, kHostedUiDepthOffset - kHostedUiShadowOffset);
shadow->SetVisible(false);
shadow->set_opacity_when_visible(1.0);
shadow->set_contributes_to_parent_bounds(false);
shadow->SetTransitionedProperties({OPACITY});
shadow->AddChild(std::move(hosted_ui));
shadow->AddBinding(std::make_unique<Binding<std::pair<bool, gfx::PointF>>>(
VR_BIND_LAMBDA(
[](Model* m) {
return std::pair<bool, gfx::PointF>(
m->hosted_platform_ui.floating,
gfx::PointF(m->hosted_platform_ui.rect.x(),
m->hosted_platform_ui.rect.y()));
},
base::Unretained(model)),
VR_BIND_LAMBDA(
[](Shadow* shadow, const std::pair<bool, gfx::PointF>& value) {
if (value.first /* floating */) {
shadow->set_x_centering(LEFT);
shadow->set_y_centering(TOP);
shadow->SetTranslate((value.second.x() - 0.5) * kContentWidth,
(0.5 - value.second.y()) * kContentHeight,
kFloatingHostedUiDistance);
shadow->set_intensity(0);
} else {
shadow->set_x_centering(NONE);
shadow->set_y_centering(NONE);
shadow->SetTranslate(
0, 0, kHostedUiDepthOffset - kHostedUiShadowOffset);
shadow->set_intensity(1);
}
},
base::Unretained(shadow.get()))));
shadow->AddBinding(VR_BIND_FUNC(bool, Model, model,
model->hosted_platform_ui.hosted_ui_enabled,
Shadow, shadow.get(), SetVisible));
auto backplane = Create<InvisibleHitTarget>(name, kPhaseForeground);
backplane->SetType(kTypeHostedUiBackplane);
backplane->SetSize(kSceneSize, kSceneSize);
backplane->SetTranslate(0.0, kContentVerticalOffset, -kContentDistance);
backplane->set_contributes_to_parent_bounds(false);
EventHandlers event_handlers;
event_handlers.button_up = base::BindRepeating(
[](Model* model, UiBrowserInterface* browser) {
if (model->hosted_platform_ui.hosted_ui_enabled) {
browser->CloseHostedDialog();
}
},
base::Unretained(model), base::Unretained(browser));
backplane->set_event_handlers(event_handlers);
backplane->AddChild(std::move(shadow));
backplane->AddBinding(
VR_BIND_FUNC(bool, Model, model,
model->hosted_platform_ui.hosted_ui_enabled &&
model->active_modal_prompt_type == kModalPromptTypeNone,
InvisibleHitTarget, backplane.get(), SetVisible));
return backplane;
}
std::unique_ptr<Grid> CreateGrid(Model* model, UiElementName name) {
auto grid = Create<Grid>(name, kPhaseBackground);
grid->set_gridline_count(kFloorGridlineCount);
grid->set_hit_testable(true);
grid->set_focusable(false);
return grid;
}
void ApplyFloorTransform(Rect* floor) {
floor->SetSize(1.0f, 1.0f);
floor->SetScale(kSceneSize, kSceneSize, kSceneSize);
floor->SetTranslate(0.0, kFloorHeight, 0.0);
floor->SetRotate(1, 0, 0, -base::kPiFloat / 2);
}
void SetVisibleInLayout(UiElement* e, bool v) {
e->SetVisible(v);
e->set_requires_layout(v);
}
std::unique_ptr<TransientElement> CreateTextToast(
UiElementName transient_parent_name,
UiElementName toast_name,
Model* model,
const base::string16& text) {
auto parent =
CreateTransientParent(transient_parent_name, kToastTimeoutSeconds, false);
parent->set_bounds_contain_children(true);
parent->SetScale(kContentDistance, kContentDistance, 1.0f);
auto background_element = Create<Rect>(toast_name, kPhaseForeground);
VR_BIND_COLOR(model, background_element.get(), &ColorScheme::toast_background,
&Rect::SetColor);
background_element->set_bounds_contain_children(true);
background_element->set_padding(kToastXPaddingDMM, kToastYPaddingDMM,
kToastXPaddingDMM, kToastYPaddingDMM);
background_element->SetTransitionedProperties({OPACITY});
background_element->SetType(kTypeToastBackground);
background_element->set_corner_radius(kToastCornerRadiusDMM);
auto text_element =
Create<Text>(kNone, kPhaseForeground, kToastTextFontHeightDMM);
text_element->SetLayoutMode(kSingleLine);
text_element->SetColor(SK_ColorWHITE);
text_element->set_owner_name_for_test(toast_name);
text_element->SetType(kTypeToastText);
text_element->SetText(text);
VR_BIND_COLOR(model, text_element.get(), &ColorScheme::toast_foreground,
&Text::SetColor);
background_element->AddChild(std::move(text_element));
parent->AddChild(std::move(background_element));
return parent;
}
std::unique_ptr<UiElement> CreateTabsView(Model* model,
UiScene* scene,
AudioDelegate* audio_delegate,
UiBrowserInterface* browser,
bool incognito) {
auto tabs_scroll_view = Create<PagedScrollView>(
kNone, kPhaseNone,
kTabsViewColumnCount * kTabItemWidthDMM +
(kTabsViewColumnCount - 1) * kTabsViewGridMarginDMM);
tabs_scroll_view->set_margin(kTabsViewGridMarginDMM);
tabs_scroll_view->set_scrollable(true);
tabs_scroll_view->set_bounds_contain_children(true);
tabs_scroll_view->SetTransitionedProperties({SCROLL_OFFSET});
auto tabs_layout = Create<PagedGridLayout>(
kNone, kPhaseNone, kTabsViewRowCount, kTabsViewColumnCount,
gfx::SizeF(kTabItemWidthDMM, kTabItemHeightDMM));
tabs_layout->set_margin(kTabsViewGridMarginDMM);
tabs_layout->set_hit_testable(true);
tabs_layout->set_bounds_contain_children(false);
tabs_layout->AddBinding(VR_BIND(
size_t, PagedScrollView, tabs_scroll_view.get(), model->current_page(),
PagedGridLayout, tabs_layout.get(), view->SetCurrentPage(value)));
TabSetBinding::ModelAddedCallback added_callback = base::BindRepeating(
&OnTabModelAdded, base::Unretained(scene), base::Unretained(model),
incognito, base::Unretained(tabs_layout.get()),
base::Unretained(audio_delegate), base::Unretained(browser));
TabSetBinding::ModelRemovedCallback removed_callback =
base::BindRepeating(&OnTabModelRemoved, base::Unretained(scene));
tabs_layout->AddBinding(std::make_unique<TabSetBinding>(
incognito ? &model->incognito_tabs : &model->regular_tabs, added_callback,
removed_callback));
tabs_scroll_view->AddScrollingChild(std::move(tabs_layout));
return tabs_scroll_view;
}
std::unique_ptr<UiElement> CreateTabModeButton(Model* model,
AudioDelegate* audio_delegate,
bool select_incognito) {
auto button = Create<TextButton>(
kNone, kPhaseForeground, kTabsViewModeButtonTextSizeDMM, audio_delegate);
button->set_click_handler(base::BindRepeating(
[](Model* model, bool select_incognito) {
model->incognito_tabs_view_selected = select_incognito;
},
base::Unretained(model), select_incognito));
button->background()->SetSize(kTabsViewModeButtonWidthDMM,
kTabsViewModeButtonHeightDMM);
button->background()->set_padding(0.0f, 0.0f);
button->background()->set_bounds_contain_children(false);
float radius = 0.5f * kTabsViewModeButtonHeightDMM;
button->SetCornerRadii(select_incognito
? CornerRadii({0, radius, 0, radius})
: CornerRadii({radius, 0, radius, 0}));
button->set_hover_offset(0);
// TODO(https://crbug.com/787654): Uppercasing should be conditional.
button->SetText(base::i18n::ToUpper(l10n_util::GetStringUTF16(
select_incognito ? IDS_VR_TABS_BUTTON_INCOGNITO
: IDS_VR_TABS_BUTTON_REGULAR)));
button->AddBinding(std::make_unique<Binding<std::pair<bool, bool>>>(
VR_BIND_LAMBDA(
[](Model* model, bool select_incognito) {
return std::make_pair(
model->incognito,
select_incognito == model->incognito_tabs_view_selected);
},
base::Unretained(model), select_incognito),
VR_BIND_LAMBDA(
[](Button* view, Model* model, const std::pair<bool, bool>& value) {
// Change color if selected.
view->SetButtonColors(
value.second ? model->color_scheme().tab_mode_button_selected
: model->color_scheme().disc_button_colors);
},
base::Unretained(button.get()), base::Unretained(model))));
return button;
}
} // namespace
UiSceneCreator::UiSceneCreator(UiBrowserInterface* browser,
UiScene* scene,
Ui* ui,
ContentInputDelegate* content_input_delegate,
KeyboardDelegate* keyboard_delegate,
TextInputDelegate* text_input_delegate,
AudioDelegate* audio_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),
audio_delegate_(audio_delegate),
model_(model) {}
UiSceneCreator::~UiSceneCreator() {}
void UiSceneCreator::CreateScene() {
Create2dBrowsingSubtreeRoots();
CreateWebVrRoot();
CreateBackground();
CreateViewportAwareRoot();
CreateContentQuad();
Create2dBrowsingHostedUi();
CreatePrompts();
CreateSystemIndicators();
CreateUrlBar();
CreateOverflowMenu();
CreateOmnibox();
CreateCloseButton();
CreateToasts();
CreateVoiceSearchUiGroup();
CreateContentRepositioningAffordance();
CreateExitWarning();
CreateWebVrSubtree();
CreateKeyboard();
CreateController();
CreateTabsViews();
}
void UiSceneCreator::Create2dBrowsingHostedUi() {
auto hosted_ui_root =
CreateHostedUi(model_, browser_, k2dBrowsingHostedUi,
k2dBrowsingHostedUiContent, kContentDistance);
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(hosted_ui_root));
}
void UiSceneCreator::Create2dBrowsingSubtreeRoots() {
auto element = Create<UiElement>(k2dBrowsingRoot, kPhaseNone);
element->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) {
return m->browsing_enabled() && !m->waiting_for_background;
},
base::Unretained(model_)),
VR_BIND_LAMBDA([](UiElement* e, const bool& v) { e->SetVisible(v); },
base::Unretained(element.get()))));
element->AddBinding(VR_BIND(
float, Model, model_, model->floor_height, UiElement, element.get(),
view->SetTranslate(0, value ? value - kFloorHeight : 0.0, 0.0)));
scene_->AddUiElement(kRoot, std::move(element));
element = Create<UiElement>(k2dBrowsingBackground, kPhaseNone);
scene_->AddUiElement(k2dBrowsingRoot, std::move(element));
auto repositioner = Create<Repositioner>(k2dBrowsingRepositioner, kPhaseNone);
repositioner->set_bounds_contain_children(true);
repositioner->AddBinding(
VR_BIND_FUNC(bool, Model, model_, model->reposition_window_enabled(),
Repositioner, repositioner.get(), SetEnabled));
repositioner->AddBinding(VR_BIND_FUNC(
gfx::Vector3dF, Model, model_, model->controller.laser_direction,
Repositioner, repositioner.get(), set_laser_direction));
repositioner->AddBinding(
VR_BIND(bool, Model, model_, model->controller.recentered, Repositioner,
repositioner.get(), if (value) { view->Reset(); }));
scene_->AddUiElement(k2dBrowsingRoot, std::move(repositioner));
auto hider = Create<UiElement>(k2dBrowsingVisibiltyHider, kPhaseNone);
hider->SetTransitionedProperties({OPACITY});
hider->SetTransitionDuration(base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
VR_BIND_VISIBILITY(
hider, model->default_browsing_enabled() || model->fullscreen_enabled());
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(hider));
auto fader = Create<UiElement>(k2dBrowsingVisibiltyFader, kPhaseNone);
fader->SetTransitionedProperties({OPACITY});
fader->SetTransitionDuration(base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
fader->AddBinding(std::make_unique<Binding<float>>(
VR_BIND_LAMBDA(
[](Model* model) {
if (model->has_mode_in_stack(kModeModalPrompt) ||
(model->hosted_platform_ui.hosted_ui_enabled &&
!model->hosted_platform_ui.floating)) {
return kModalPromptFadeOpacity;
}
return 1.0f;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, const float& value) { e->SetOpacity(value); },
base::Unretained(fader.get()))));
scene_->AddUiElement(k2dBrowsingVisibiltyHider, std::move(fader));
element = Create<UiElement>(k2dBrowsingForeground, kPhaseNone);
element->set_bounds_contain_children(true);
element->SetTransitionedProperties({OPACITY});
element->SetTransitionDuration(base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
scene_->AddUiElement(k2dBrowsingVisibiltyFader, std::move(element));
element = Create<UiElement>(k2dBrowsingContentGroup, kPhaseNone);
element->SetTranslate(0, kContentVerticalOffset, -kContentDistance);
element->SetTransitionedProperties({TRANSFORM});
element->set_bounds_contain_children(true);
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);
VR_BIND_VISIBILITY(element, model->web_vr_enabled());
element->AddBinding(VR_BIND(
float, Model, model_, model->floor_height, UiElement, element.get(),
view->SetTranslate(0, value ? value - kFloorHeight : 0.0, 0.0)));
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->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->SetFieldWidth(kExitWarningTextWidthDMM);
exit_warning_text->SetVisible(true);
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->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() {
auto backplane =
Create<InvisibleHitTarget>(kIndicatorBackplane, kPhaseForeground);
backplane->set_bounds_contain_children(true);
backplane->set_contributes_to_parent_bounds(false);
backplane->set_y_anchoring(TOP);
backplane->set_corner_radius(kIndicatorCornerRadiusDMM);
backplane->SetTranslate(0, kIndicatorVerticalOffset,
kIndicatorDistanceOffset);
backplane->SetScale(kIndicatorDepth, kIndicatorDepth, 1.0f);
VR_BIND_VISIBILITY(backplane, !model->fullscreen_enabled());
auto indicator_layout =
Create<LinearLayout>(kIndicatorLayout, kPhaseNone, LinearLayout::kRight);
indicator_layout->set_margin(kIndicatorMarginDMM);
indicator_layout->set_contributes_to_parent_bounds(false);
auto* content_frame = scene_->GetUiElementByName(kContentFrame);
content_frame->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](UiElement* plane, UiElement* indicators) {
if (static_cast<InvisibleHitTarget*>(plane)->hovered())
return true;
for (auto& child : indicators->children()) {
if (static_cast<Button*>(child.get())->hovered())
return true;
}
return false;
},
base::Unretained(scene_->GetUiElementByName(kContentFrameHitPlane)),
base::Unretained(indicator_layout.get())),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& value) {
static_cast<Rect*>(e)->SetLocalOpacity(value ? 1.0f : 0.0f);
},
base::Unretained(content_frame))));
auto specs = GetIndicatorSpecs();
for (const auto& spec : specs) {
auto element = std::make_unique<VectorIconButton>(
base::RepeatingCallback<void()>(), spec.icon, audio_delegate_);
element->SetName(spec.name);
element->SetDrawPhase(kPhaseForeground);
element->SetSize(kIndicatorHeightDMM, kIndicatorHeightDMM);
element->SetIconScaleFactor(kIndicatorIconScaleFactor);
element->set_hover_offset(0.0f);
element->SetSounds(Sounds(), audio_delegate_);
element->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* model, bool CapturingStateModel::*signal) {
return model->active_capturing.*signal ||
model->background_capturing.*signal;
},
base::Unretained(model_), spec.signal),
VR_BIND_LAMBDA(
[](UiElement* view, const bool& value) {
view->SetVisible(value);
view->set_requires_layout(value);
},
base::Unretained(element.get()))));
element->AddBinding(std::make_unique<Binding<std::pair<bool, bool>>>(
VR_BIND_LAMBDA(
[](UiElement* parent, UiElement* child) {
return std::make_pair(parent->FirstLaidOutChild() == child,
parent->LastLaidOutChild() == child);
},
base::Unretained(indicator_layout.get()),
base::Unretained(element.get())),
VR_BIND_LAMBDA(
[](UiElement* view, const std::pair<bool, bool>& value) {
CornerRadii radii;
radii.upper_left = value.first ? kIndicatorCornerRadiusDMM : 0.0f;
radii.lower_left = radii.upper_left;
radii.upper_right =
value.second ? kIndicatorCornerRadiusDMM : 0.0f;
radii.lower_right = radii.upper_right;
view->SetCornerRadii(radii);
},
base::Unretained(element.get()))));
VR_BIND_BUTTON_COLORS(model_, element.get(), &ColorScheme::indicator,
&Button::SetButtonColors);
auto tooltip = Create<Oval>(kNone, kPhaseForeground);
VR_BIND_COLOR(model_, tooltip.get(),
&ColorScheme::webvr_permission_background, &Rect::SetColor);
tooltip->set_bounds_contain_children(true);
tooltip->set_contributes_to_parent_bounds(false);
tooltip->set_padding(kIndicatorXPaddingDMM, kIndicatorYPaddingDMM,
kIndicatorXPaddingDMM, kIndicatorYPaddingDMM);
tooltip->set_y_anchoring(BOTTOM);
tooltip->set_y_centering(TOP);
tooltip->SetVisible(false);
tooltip->SetTranslate(0, kIndicatorOffsetDMM, 0);
tooltip->set_owner_name_for_test(element->name());
tooltip->SetTransitionedProperties({OPACITY});
tooltip->SetType(kTypeTooltip);
tooltip->AddBinding(VR_BIND_FUNC(bool, Button, element.get(),
model->hovered(), UiElement, tooltip.get(),
SetVisible));
auto text_element =
Create<Text>(kNone, kPhaseForeground, kWebVrPermissionFontHeight);
text_element->SetLayoutMode(kSingleLine);
text_element->SetColor(SK_ColorWHITE);
text_element->set_owner_name_for_test(element->name());
text_element->SetType(kTypeLabel);
BindIndicatorText(model_, text_element.get(), spec);
VR_BIND_COLOR(model_, text_element.get(),
&ColorScheme::webvr_permission_foreground, &Text::SetColor);
tooltip->AddChild(std::move(text_element));
element->AddChild(std::move(tooltip));
indicator_layout->AddChild(std::move(element));
}
backplane->AddChild(std::move(indicator_layout));
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(backplane));
}
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 = Create<InvisibleHitTarget>(kBackplane, kPhaseBackplanes);
hit_plane->SetSize(kBackplaneSize, kSceneHeight);
hit_plane->set_contributes_to_parent_bounds(false);
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(hit_plane));
auto resizer = Create<Resizer>(kContentResizer, kPhaseNone);
resizer->AddBinding(VR_BIND_FUNC(bool, Model, model_,
model->reposition_window_enabled(), Resizer,
resizer.get(), SetEnabled));
resizer->AddBinding(VR_BIND_FUNC(gfx::PointF, Model, model_,
model->controller.touchpad_touch_position,
Resizer, resizer.get(), set_touch_position));
resizer->AddBinding(VR_BIND_FUNC(bool, Model, model_,
model->controller.touching_touchpad, Resizer,
resizer.get(), SetTouchingTouchpad));
auto main_content = std::make_unique<ContentElement>(
content_input_delegate_,
base::BindRepeating(&UiBrowserInterface::OnContentScreenBoundsChanged,
base::Unretained(browser_)));
EventHandlers event_handlers;
event_handlers.focus_change = base::BindRepeating(
[](Model* model, ContentElement* e, ContentInputDelegate* delegate,
bool focused) {
if (!focused) {
model->web_input_text_field_info = EditedText();
delegate->ClearTextInputState();
}
e->UpdateInput(model->web_input_text_field_info);
},
model_, base::Unretained(main_content.get()),
base::Unretained(content_input_delegate_));
main_content->set_event_handlers(event_handlers);
main_content->SetName(kContentQuad);
main_content->set_hit_testable(true);
main_content->SetDrawPhase(kPhaseForeground);
main_content->SetSize(kContentWidth, kContentHeight);
main_content->set_corner_radius(kContentCornerRadius);
main_content->SetTransitionedProperties({BOUNDS});
main_content->SetTextInputDelegate(text_input_delegate_);
main_content->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return m->editing_web_input; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](ContentElement* e, const bool& v) {
if (v) {
e->RequestFocus();
} else {
e->RequestUnfocus();
}
},
base::Unretained(main_content.get()))));
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));
main_content->AddBinding(VR_BIND_FUNC(
unsigned int, Model, model_, model->content_overlay_texture_id,
ContentElement, main_content.get(), SetOverlayTextureId));
main_content->AddBinding(
VR_BIND_FUNC(UiElementRenderer::TextureLocation, Model, model_,
model->content_overlay_location, ContentElement,
main_content.get(), SetOverlayTextureLocation));
main_content->AddBinding(VR_BIND_FUNC(
bool, Model, model_, !model->content_overlay_texture_non_empty,
ContentElement, main_content.get(), SetOverlayTextureEmpty));
main_content->AddBinding(std::make_unique<Binding<EditedText>>(
VR_BIND_LAMBDA([](EditedText* info) { return *info; },
base::Unretained(&model_->web_input_text_field_info)),
VR_BIND_LAMBDA([](ContentElement* e,
const EditedText& value) { e->UpdateInput(value); },
base::Unretained(main_content.get()))));
auto indicator_bg = Create<Rect>(kLoadingIndicator, kPhaseForeground);
indicator_bg->set_contributes_to_parent_bounds(false);
indicator_bg->set_bounds_contain_children(true);
indicator_bg->set_y_anchoring(BOTTOM);
indicator_bg->set_y_centering(BOTTOM);
indicator_bg->SetTranslate(0, kLoadingIndicatorYOffset, 0);
indicator_bg->SetCornerRadii(
{0, 0, kContentCornerRadius, kContentCornerRadius});
indicator_bg->SetTransitionedProperties({OPACITY});
VR_BIND_VISIBILITY(indicator_bg, model->loading);
VR_BIND_COLOR(model_, indicator_bg.get(),
&ColorScheme::loading_indicator_background, &Rect::SetColor);
auto indicator_fg =
Create<Rect>(kLoadingIndicatorForeground, kPhaseForeground);
indicator_fg->SetCornerRadii(
{0, 0, kContentCornerRadius, kContentCornerRadius});
// Start at content width; size is later updated in a content callback.
indicator_fg->SetSize(kContentWidth, kLoadingIndicatorHeight);
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& progress) {
r->SetClipRect({0, 0, progress, 1});
},
base::Unretained(indicator_fg.get()))));
// Have content explicitly resize the loading indicator as it changes, in lieu
// of adding a UI framework capability.
main_content->set_on_size_changed_callback(base::BindRepeating(
[](Rect* rect, const gfx::SizeF& size) {
rect->SetSize(size.width(), kLoadingIndicatorHeight);
},
base::Unretained(indicator_fg.get())));
indicator_bg->AddChild(std::move(indicator_fg));
main_content->AddChild(std::move(indicator_bg));
auto frame = Create<Rect>(kContentFrame, kPhaseForeground);
frame->set_hit_testable(true);
frame->set_bounds_contain_children(true);
frame->set_padding(kRepositionFrameEdgePadding, kRepositionFrameTopPadding,
kRepositionFrameEdgePadding, kRepositionFrameEdgePadding);
frame->set_corner_radius(kContentCornerRadius);
frame->set_bounds_contain_padding(false);
frame->SetLocalOpacity(0.0f);
frame->SetTransitionedProperties({LOCAL_OPACITY});
frame->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kRepositionFrameTransitionDurationMs));
VR_BIND_COLOR(model_, frame.get(), &ColorScheme::content_reposition_frame,
&Rect::SetColor);
auto plane =
Create<InvisibleHitTarget>(kContentFrameHitPlane, kPhaseForeground);
plane->set_bounds_contain_children(true);
plane->set_bounds_contain_padding(false);
plane->set_corner_radius(kContentCornerRadius);
plane->set_cursor_type(kCursorReposition);
Sounds sounds;
sounds.button_up = kSoundButtonClick;
plane->SetSounds(sounds, audio_delegate_);
plane->set_padding(0, kRepositionFrameHitPlaneTopPadding, 0, 0);
plane->set_event_handlers(CreateRepositioningHandlers(model_, scene_));
plane->AddBinding(VR_BIND_FUNC(bool, Model, model_,
model->reposition_window_permitted(),
UiElement, plane.get(), set_hit_testable));
resizer->AddChild(std::move(main_content));
plane->AddChild(std::move(resizer));
frame->AddChild(std::move(plane));
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(frame));
// Limit reticle distance to a sphere based on maximum content distance.
scene_->set_background_distance(kFullscreenDistance *
kBackgroundDistanceMultiplier);
}
void UiSceneCreator::CreateWebVrSubtree() {
CreateWebVrOverlayElements();
CreateWebVrTimeoutScreen();
// This is needed to for accepting permissions in WebVR mode.
auto hosted_ui_root =
CreateHostedUi(model_, browser_, kWebVrHostedUi, kWebVrHostedUiContent,
kTimeoutScreenDisatance);
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(hosted_ui_root));
// 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->SetColor(model_->color_scheme().web_vr_background);
bg->SetTransitionedProperties({OPACITY});
VR_BIND_VISIBILITY(
bg, model->web_vr_enabled() && (!model->web_vr.presenting_web_vr() ||
model->web_vr.showing_hosted_ui));
auto grid = CreateGrid(model_, kNone);
grid->set_owner_name_for_test(kWebVrFloor);
VR_BIND_COLOR(model_, grid.get(), &ColorScheme::web_vr_floor_grid,
&Grid::SetGridColor);
auto grid_bg = Create<Rect>(kWebVrFloor, kPhaseBackground);
ApplyFloorTransform(grid_bg.get());
VR_BIND_COLOR(model_, grid_bg.get(), &ColorScheme::web_vr_floor_center,
&Rect::SetCenterColor);
VR_BIND_COLOR(model_, grid_bg.get(), &ColorScheme::web_vr_floor_edge,
&Rect::SetEdgeColor);
grid_bg->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* model, UiElement* timeout_screen) {
return model->web_vr_enabled() &&
(model->web_vr.showing_hosted_ui ||
timeout_screen->GetTargetOpacity() != 0.f);
},
base::Unretained(model_),
base::Unretained(scene_->GetUiElementByName(kWebVrTimeoutRoot))),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& value) { e->SetVisible(value); },
base::Unretained(grid_bg.get()))));
grid_bg->AddChild(std::move(grid));
bg->AddChild(std::move(grid_bg));
scene_->AddUiElement(kWebVrRoot, std::move(bg));
}
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) {
return (model->web_vr.state == kWebVrTimeoutImminent ||
model->web_vr.state == kWebVrTimedOut);
},
base::Unretained(model_)),
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);
spinner->set_hit_testable(true);
VR_BIND_VISIBILITY(spinner, model->web_vr.state == kWebVrTimeoutImminent);
auto timeout_message = Create<Rect>(kWebVrTimeoutMessage, kPhaseForeground);
timeout_message->SetVisible(false);
timeout_message->set_hit_testable(true);
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_margin(kTimeoutMessageLayoutGapDMM);
auto timeout_icon =
Create<VectorIcon>(kWebVrTimeoutMessageIcon, kPhaseForeground, 512);
timeout_icon->SetIcon(kSadTabIcon);
timeout_icon->set_hit_testable(true);
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(kTextAlignmentLeft);
timeout_text->SetFieldWidth(kTimeoutMessageTextWidthDMM);
timeout_text->set_hit_testable(true);
auto button_scaler =
std::make_unique<ScaledDepthAdjuster>(kTimeoutButtonDepthOffset);
auto button =
Create<DiscButton>(kWebVrTimeoutMessageButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::ExitPresent,
base::Unretained(browser_)),
vector_icons::kCloseRoundedIcon, audio_delegate_);
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::disc_button_colors,
&DiscButton::SetButtonColors);
auto timeout_button_text =
Create<Text>(kWebVrTimeoutMessageButtonText, kPhaseForeground,
kTimeoutMessageTextFontHeightDMM);
// Disc-style button text is not uppercase. See https://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->SetFieldWidth(kTimeoutButtonTextWidthDMM);
timeout_button_text->set_contributes_to_parent_bounds(false);
timeout_button_text->set_y_anchoring(BOTTOM);
timeout_button_text->SetTranslate(0, -kTimeoutButtonTextVerticalOffsetDMM, 0);
timeout_button_text->set_hit_testable(true);
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::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 stars = Create<Stars>(kStars, kPhaseBackground);
stars->SetRotate(1, 0, 0, base::kPiFloat * 0.5);
scene_->AddUiElement(k2dBrowsingTexturedBackground, std::move(stars));
auto grid = CreateGrid(model_, kNone);
ApplyFloorTransform(grid.get());
VR_BIND_COLOR(model_, grid.get(), &ColorScheme::floor_grid,
&Grid::SetGridColor);
grid->SetOpacity(kGridOpacity);
scene_->AddUiElement(k2dBrowsingTexturedBackground, std::move(grid));
// Fallback background.
auto element = Create<UiElement>(k2dBrowsingDefaultBackground, kPhaseNone);
VR_BIND_VISIBILITY(element, !model->background_loaded);
scene_->AddUiElement(k2dBrowsingBackground, std::move(element));
auto solid_background =
Create<FullScreenRect>(kSolidBackground, kPhaseBackground);
VR_BIND_COLOR(model_, solid_background.get(), &ColorScheme::world_background,
&Rect::SetColor);
VR_BIND_VISIBILITY(solid_background, model->browsing_enabled());
scene_->AddUiElement(k2dBrowsingDefaultBackground,
std::move(solid_background));
auto grid_fallback = CreateGrid(model_, kNone);
grid_fallback->set_owner_name_for_test(kFloor);
VR_BIND_COLOR(model_, grid_fallback.get(), &ColorScheme::floor_grid,
&Grid::SetGridColor);
auto floor = Create<Rect>(kFloor, kPhaseBackground);
ApplyFloorTransform(floor.get());
VR_BIND_COLOR(model_, floor.get(), &ColorScheme::floor,
&Rect::SetCenterColor);
VR_BIND_COLOR(model_, floor.get(), &ColorScheme::world_background,
&Rect::SetEdgeColor);
floor->AddChild(std::move(grid_fallback));
scene_->AddUiElement(k2dBrowsingDefaultBackground, std::move(floor));
// Ceiling.
auto ceiling = Create<Rect>(kCeiling, kPhaseBackground);
ceiling->set_hit_testable(true);
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);
scene_->AddUiElement(kWebVrRoot, std::move(element));
element = std::make_unique<ViewportAwareRoot>();
element->SetName(k2dBrowsingViewportAwareRoot);
element->set_contributes_to_parent_bounds(false);
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(element));
}
void UiSceneCreator::CreateVoiceSearchUiGroup() {
auto speech_recognition_root = std::make_unique<UiElement>();
speech_recognition_root->SetName(kSpeechRecognitionRoot);
speech_recognition_root->SetVisible(false);
speech_recognition_root->set_contributes_to_parent_bounds(false);
speech_recognition_root->SetTranslate(0.f, 0.f, -kContentDistance);
speech_recognition_root->SetTransitionedProperties({OPACITY});
speech_recognition_root->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(
kSpeechRecognitionOpacityAnimationDurationMs));
VR_BIND_VISIBILITY(speech_recognition_root, model->voice_search_enabled());
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);
VR_BIND_COLOR(model_, inner_circle.get(),
&ColorScheme::speech_recognition_circle_background,
&Rect::SetColor);
auto microphone_icon = std::make_unique<VectorIcon>(512);
microphone_icon->SetIcon(vector_icons::kMicIcon);
microphone_icon->SetName(kSpeechRecognitionMicrophoneIcon);
microphone_icon->SetDrawPhase(kPhaseForeground);
microphone_icon->SetSize(kCloseButtonDiameter, kCloseButtonDiameter);
auto speech_result_parent =
Create<UiElement>(kSpeechRecognitionResult, kPhaseNone);
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->SetVisibleImmediately(true);
else
e->SetVisible(false);
},
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->SetFieldWidth(kVoiceSearchRecognitionResultTextWidth);
speech_result->SetAlignment(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(kBackplaneSize, kBackplaneSize);
speech_result_parent->AddChild(std::move(hit_target));
auto speech_recognition_listening = std::make_unique<UiElement>();
speech_recognition_listening->SetName(kSpeechRecognitionListening);
VR_BIND_VISIBILITY(speech_recognition_listening,
model->speech.recognition_result.empty());
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);
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)));
auto close_button = Create<DiscButton>(
kSpeechRecognitionListeningCloseButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::SetVoiceSearchActive,
base::Unretained(browser_), false),
vector_icons::kCloseRoundedIcon, audio_delegate_);
close_button->SetSize(kVoiceSearchCloseButtonDiameter,
kVoiceSearchCloseButtonDiameter);
close_button->set_hover_offset(kButtonZOffsetHoverDMM * kContentDistance);
close_button->SetTranslate(0, -kVoiceSearchCloseButtonYOffset, 0);
close_button->SetRotate(
1, 0, 0, atan(-kVoiceSearchCloseButtonYOffset / kContentDistance));
VR_BIND_BUTTON_COLORS(model_, close_button.get(),
&ColorScheme::disc_button_colors,
&DiscButton::SetButtonColors);
speech_recognition_listening->AddChild(std::move(growing_circle));
speech_recognition_listening->AddChild(std::move(close_button));
speech_recognition_root->AddChild(std::move(inner_circle));
speech_recognition_root->AddChild(std::move(microphone_icon));
speech_recognition_root->AddChild(std::move(speech_recognition_listening));
speech_recognition_root->AddChild(std::move(speech_result_parent));
scene_->AddUiElement(k2dBrowsingRepositioner,
std::move(speech_recognition_root));
}
void UiSceneCreator::CreateContentRepositioningAffordance() {
auto content_toggle =
Create<UiElement>(kContentRepositionVisibilityToggle, kPhaseNone);
content_toggle->SetTransitionedProperties({OPACITY});
content_toggle->set_bounds_contain_children(true);
content_toggle->AddBinding(VR_BIND_FUNC(
float, Model, model_,
model->reposition_window_enabled() ? kRepositionContentOpacity : 1.0f,
UiElement, content_toggle.get(), SetOpacity));
scene_->AddParentUiElement(k2dBrowsingForeground,
std::move(content_toggle));
auto hit_plane =
Create<InvisibleHitTarget>(kContentRepositionHitPlane, kPhaseForeground);
hit_plane->set_contributes_to_parent_bounds(false);
hit_plane->SetSize(kSceneSize, kSceneSize);
hit_plane->SetTranslate(0.0f, 0.0f, -kContentDistance);
hit_plane->set_cursor_type(kCursorReposition);
Sounds sounds;
sounds.button_up = kSoundButtonClick;
hit_plane->SetSounds(sounds, audio_delegate_);
EventHandlers event_handlers;
event_handlers.button_up = base::BindRepeating(
[](Model* m) {
DCHECK(m->reposition_window_enabled());
m->pop_mode(kModeRepositionWindow);
},
base::Unretained(model_));
hit_plane->set_event_handlers(event_handlers);
VR_BIND_VISIBILITY(hit_plane, model->reposition_window_enabled());
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(hit_plane));
}
void UiSceneCreator::CreateController() {
auto root = std::make_unique<UiElement>();
root->SetName(kControllerRoot);
VR_BIND_VISIBILITY(root, model->browsing_enabled() ||
model->web_vr.state == kWebVrTimedOut ||
model->hosted_platform_ui.hosted_ui_enabled);
scene_->AddUiElement(kRoot, std::move(root));
auto group = std::make_unique<UiElement>();
group->SetName(kControllerGroup);
scene_->AddUiElement(kControllerRoot, std::move(group));
auto controller = CreateControllerElement(model_);
auto callout_group = Create<UiElement>(kNone, kPhaseNone);
callout_group->SetVisible(false);
callout_group->SetTransitionedProperties({OPACITY});
callout_group->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kControllerLabelTransitionDurationMs));
VR_BIND_VISIBILITY(callout_group, model->controller.resting_in_viewport);
auto trackpad_button = CreateControllerLabel(
kControllerTrackpadLabel, kControllerTrackpadOffset,
l10n_util::GetStringUTF16(IDS_VR_BUTTON_TRACKPAD), model_);
VR_BIND_VISIBILITY(trackpad_button, !model->reposition_window_enabled());
callout_group->AddChild(std::move(trackpad_button));
auto reposition_button = CreateControllerLabel(
kControllerTrackpadRepositionLabel, kControllerTrackpadOffset,
l10n_util::GetStringUTF16(IDS_VR_BUTTON_TRACKPAD_REPOSITION), model_);
VR_BIND_VISIBILITY(reposition_button, model->reposition_window_enabled());
callout_group->AddChild(std::move(reposition_button));
auto exit_button_label = CreateControllerLabel(
kControllerExitButtonLabel, kControllerExitButtonOffset,
l10n_util::GetStringUTF16(IDS_VR_BUTTON_EXIT), model_);
VR_BIND_VISIBILITY(exit_button_label, model->fullscreen_enabled());
callout_group->AddChild(std::move(exit_button_label));
auto back_button_label = CreateControllerLabel(
kControllerBackButtonLabel, kControllerBackButtonOffset,
l10n_util::GetStringUTF16(IDS_VR_BUTTON_BACK), model_);
VR_BIND_VISIBILITY(back_button_label, model->omnibox_editing_enabled() ||
model->voice_search_enabled());
callout_group->AddChild(std::move(back_button_label));
auto reposition_finish_button = CreateControllerLabel(
kControllerRepositionFinishLabel, kControllerBackButtonOffset,
l10n_util::GetStringUTF16(IDS_VR_BUTTON_APP_REPOSITION), model_);
VR_BIND_VISIBILITY(reposition_finish_button,
model->reposition_window_enabled());
callout_group->AddChild(std::move(reposition_finish_button));
controller->AddChild(std::move(callout_group));
scene_->AddUiElement(kControllerGroup, std::move(controller));
auto reticle_laser_group = Create<UiElement>(kReticleLaserGroup, kPhaseNone);
reticle_laser_group->SetTransitionedProperties({OPACITY});
reticle_laser_group->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kControllerLabelTransitionDurationMs));
VR_BIND_VISIBILITY(reticle_laser_group, !model->reposition_window_enabled());
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));
auto reticle = std::make_unique<Reticle>(scene_, model_);
reticle->SetDrawPhase(kPhaseForeground);
VR_BIND_VISIBILITY(reticle, model->reticle.target_point != gfx::Point3F());
auto reposition_group = Create<UiElement>(kRepositionCursor, kPhaseNone);
VR_BIND_VISIBILITY(reposition_group,
model->reticle.cursor_type == kCursorReposition);
auto reposition_bg = Create<Rect>(kNone, kPhaseForeground);
reposition_bg->set_owner_name_for_test(kRepositionCursor);
reposition_bg->SetType(kTypeCursorBackground);
reposition_bg->SetSize(kRepositionCursorBackgroundSize,
kRepositionCursorBackgroundSize);
reposition_bg->SetDrawPhase(kPhaseForeground);
VR_BIND_COLOR(model_, reposition_bg.get(),
&ColorScheme::cursor_background_edge, &Rect::SetEdgeColor);
VR_BIND_COLOR(model_, reposition_bg.get(),
&ColorScheme::cursor_background_center, &Rect::SetCenterColor);
auto reposition_icon = std::make_unique<VectorIcon>(128);
reposition_icon->set_owner_name_for_test(kRepositionCursor);
reposition_icon->SetType(kTypeCursorForeground);
reposition_icon->SetIcon(kRepositionIcon);
reposition_icon->SetDrawPhase(kPhaseForeground);
reposition_icon->SetSize(kRepositionCursorSize, kRepositionCursorSize);
VR_BIND_COLOR(model_, reposition_icon.get(), &ColorScheme::cursor_foreground,
&VectorIcon::SetColor);
reposition_group->AddChild(std::move(reposition_bg));
reposition_group->AddChild(std::move(reposition_icon));
reticle->AddChild(std::move(reposition_group));
reticle_laser_group->AddChild(std::move(laser));
reticle_laser_group->AddChild(std::move(reticle));
scene_->AddUiElement(kControllerGroup, std::move(reticle_laser_group));
}
void UiSceneCreator::CreateKeyboard() {
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);
keyboard->AddBinding(std::make_unique<Binding<std::pair<bool, gfx::PointF>>>(
VR_BIND_LAMBDA(
[](Model* m) {
return std::pair<bool, gfx::PointF>(
m->controller.touching_touchpad,
m->controller.touchpad_touch_position);
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Keyboard* keyboard, const std::pair<bool, gfx::PointF>& value) {
keyboard->OnTouchStateUpdated(value.first, value.second);
},
base::Unretained(keyboard.get()))));
keyboard->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return m->editing_web_input; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& enabled) {
if (enabled) {
e->SetTranslate(
0.0, kKeyboardVerticalOffsetDMM * kKeyboardWebInputOffset,
0.0);
} else {
e->SetTranslate(0.0, kKeyboardVerticalOffsetDMM, 0.0);
}
},
base::Unretained(keyboard.get()))));
VR_BIND_VISIBILITY(keyboard, (model->editing_input ||
(model->editing_web_input &&
(model->get_mode() == kModeBrowsing ||
model->get_mode() == kModeFullscreen))));
scene_->AddPerFrameCallback(base::BindRepeating(
[](Keyboard* keyboard) { keyboard->AdvanceKeyboardFrameIfNeeded(); },
base::Unretained(keyboard.get())));
scaler->AddChild(std::move(keyboard));
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(scaler));
}
void UiSceneCreator::CreateUrlBar() {
auto positioner = Create<UiElement>(kUrlBarPositioner, kPhaseNone);
positioner->set_y_anchoring(BOTTOM);
positioner->SetTranslate(0, kUrlBarRelativeOffset, 0);
positioner->set_contributes_to_parent_bounds(false);
scene_->AddUiElement(k2dBrowsingForeground, std::move(positioner));
auto scaler = std::make_unique<ScaledDepthAdjuster>(kUrlBarDistance);
scaler->SetName(kUrlBarDmmRoot);
scaler->set_contributes_to_parent_bounds(false);
scene_->AddUiElement(kUrlBarPositioner, std::move(scaler));
auto url_bar = Create<Rect>(kUrlBar, kPhaseForeground);
url_bar->SetRotate(1, 0, 0, kUrlBarRotationRad);
url_bar->set_bounds_contain_children(true);
url_bar->set_corner_radius(kUrlBarHeightDMM / 2);
url_bar->SetTransitionedProperties({FOREGROUND_COLOR, BACKGROUND_COLOR});
VR_BIND_VISIBILITY(url_bar, !model->fullscreen_enabled());
VR_BIND_COLOR(model_, url_bar.get(), &ColorScheme::url_bar_background,
&Rect::SetColor);
scene_->AddUiElement(kUrlBarDmmRoot, std::move(url_bar));
auto layout =
Create<LinearLayout>(kUrlBarLayout, kPhaseNone, LinearLayout::kRight);
layout->set_bounds_contain_children(true);
scene_->AddUiElement(kUrlBar, std::move(layout));
auto back_button = Create<VectorIconButton>(
kUrlBarBackButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::NavigateBack,
base::Unretained(browser_)),
vector_icons::kBackArrowIcon, audio_delegate_);
back_button->SetSize(kUrlBarEndButtonWidthDMM, kUrlBarHeightDMM);
back_button->SetCornerRadii(
{kUrlBarHeightDMM / 2, 0, kUrlBarHeightDMM / 2, 0});
back_button->set_hover_offset(0);
back_button->SetIconScaleFactor(kUrlBarButtonIconSizeDMM / kUrlBarHeightDMM);
back_button->SetIconTranslation(kUrlBarEndButtonIconOffsetDMM, 0);
back_button->AddBinding(VR_BIND_FUNC(bool, Model, model_,
model->can_navigate_back, Button,
back_button.get(), SetEnabled));
VR_BIND_BUTTON_COLORS(model_, back_button.get(), &ColorScheme::url_bar_button,
&Button::SetButtonColors);
scene_->AddUiElement(kUrlBarLayout, std::move(back_button));
auto separator = Create<Rect>(kUrlBarLeftSeparator, kPhaseForeground);
separator->set_hit_testable(true);
separator->SetSize(kUrlBarSeparatorWidthDMM, kUrlBarHeightDMM);
VR_BIND_COLOR(model_, separator.get(), &ColorScheme::url_bar_separator,
&Rect::SetColor);
scene_->AddUiElement(kUrlBarLayout, std::move(separator));
auto url_click_callback = base::BindRepeating(
[](Model* model, UiBrowserInterface* browser) {
if (model->needs_keyboard_update) {
browser->OnUnsupportedMode(UiUnsupportedMode::kNeedsKeyboardUpdate);
} else {
model->push_mode(kModeEditingOmnibox);
}
},
base::Unretained(model_), base::Unretained(browser_));
auto origin_region = Create<Button>(kUrlBarOriginRegion, kPhaseForeground,
url_click_callback, audio_delegate_);
origin_region->set_hit_testable(true);
origin_region->set_bounds_contain_children(true);
origin_region->set_hover_offset(0);
VR_BIND_BUTTON_COLORS(model_, origin_region.get(),
&ColorScheme::url_bar_button, &Button::SetButtonColors);
scene_->AddUiElement(kUrlBarLayout, std::move(origin_region));
// This layout contains the page info icon and URL.
auto origin_layout = Create<LinearLayout>(kUrlBarOriginLayout, kPhaseNone,
LinearLayout::kRight);
VR_BIND_VISIBILITY(origin_layout, model->toolbar_state.should_display_url);
scene_->AddUiElement(kUrlBarOriginRegion, std::move(origin_layout));
// This layout contains hint-text items, shown when there's no origin.
auto hint_layout =
Create<LinearLayout>(kUrlBarHintLayout, kPhaseNone, LinearLayout::kRight);
VR_BIND_VISIBILITY(hint_layout, !model->toolbar_state.should_display_url);
scene_->AddUiElement(kUrlBarOriginRegion, std::move(hint_layout));
auto security_button_region =
Create<Rect>(kUrlBarSecurityButtonRegion, kPhaseNone);
security_button_region->SetType(kTypeSpacer);
security_button_region->SetSize(kUrlBarEndButtonWidthDMM, kUrlBarHeightDMM);
scene_->AddUiElement(kUrlBarOriginLayout, std::move(security_button_region));
auto security_button = Create<VectorIconButton>(
kUrlBarSecurityButton, kPhaseForeground,
base::BindRepeating(&UiBrowserInterface::ShowPageInfo,
base::Unretained(browser_)),
toolbar::kHttpsInvalidIcon, audio_delegate_);
security_button->SetIconScaleFactor(kUrlBarButtonIconScaleFactor);
security_button->SetSize(kUrlBarButtonSizeDMM, kUrlBarButtonSizeDMM);
security_button->set_corner_radius(kUrlBarItemCornerRadiusDMM);
security_button->set_hover_offset(kUrlBarButtonHoverOffsetDMM);
VR_BIND_BUTTON_COLORS(model_, security_button.get(),
&ColorScheme::url_bar_button, &Button::SetButtonColors);
security_button->AddBinding(std::make_unique<Binding<const gfx::VectorIcon*>>(
VR_BIND_LAMBDA([](Model* m) { return m->toolbar_state.vector_icon; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](VectorIconButton* e, const gfx::VectorIcon* const& icon) {
if (icon != nullptr) {
e->SetIcon(*icon);
}
},
security_button.get())));
security_button->AddBinding(std::make_unique<Binding<ButtonColors>>(
VR_BIND_LAMBDA(
[](Model* m) {
ButtonColors colors = m->color_scheme().url_bar_button;
if (m->toolbar_state.security_level ==
security_state::SecurityLevel::DANGEROUS) {
colors.foreground = m->color_scheme().url_bar_dangerous_icon;
}
return colors;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](VectorIconButton* e, const ButtonColors& colors) {
e->SetButtonColors(colors);
},
base::Unretained(security_button.get()))));
scene_->AddUiElement(kUrlBarSecurityButtonRegion, std::move(security_button));
auto url_text = Create<UrlText>(
kUrlBarUrlText, kPhaseForeground, kUrlBarFontHeightDMM,
base::BindRepeating(&UiBrowserInterface::OnUnsupportedMode,
base::Unretained(browser_),
UiUnsupportedMode::kUnhandledCodePoint));
url_text->SetFieldWidth(kUrlBarUrlWidthDMM);
url_text->AddBinding(VR_BIND_FUNC(GURL, Model, model_,
model->toolbar_state.gurl, UrlText,
url_text.get(), SetUrl));
VR_BIND_COLOR(model_, url_text.get(), &ColorScheme::url_text_emphasized,
&UrlText::SetEmphasizedColor);
VR_BIND_COLOR(model_, url_text.get(), &ColorScheme::url_text_deemphasized,
&UrlText::SetDeemphasizedColor);
scene_->AddUiElement(kUrlBarOriginLayout, std::move(url_text));
auto right_margin = Create<Rect>(kNone, kPhaseNone);
right_margin->SetType(kTypeSpacer);
right_margin->SetSize(kUrlBarOriginRightMarginDMM, 0);
scene_->AddUiElement(kUrlBarOriginLayout, std::move(right_margin));
auto hint_text_spacer = Create<Rect>(kNone, kPhaseNone);
hint_text_spacer->SetType(kTypeSpacer);
hint_text_spacer->SetSize(kUrlBarOriginContentOffsetDMM, kUrlBarHeightDMM);
scene_->AddUiElement(kUrlBarHintLayout, std::move(hint_text_spacer));
auto hint_text =
Create<Text>(kUrlBarHintText, kPhaseForeground, kUrlBarFontHeightDMM);
hint_text->SetFieldWidth(kUrlBarOriginRegionWidthDMM -
kUrlBarOriginContentOffsetDMM);
hint_text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
hint_text->SetAlignment(kTextAlignmentLeft);
hint_text->SetText(l10n_util::GetStringUTF16(IDS_SEARCH_OR_TYPE_WEB_ADDRESS));
VR_BIND_COLOR(model_, hint_text.get(), &ColorScheme::url_bar_hint_text,
&Text::SetColor);
scene_->AddUiElement(kUrlBarHintLayout, std::move(hint_text));
separator = Create<Rect>(kUrlBarRightSeparator, kPhaseForeground);
separator->set_hit_testable(true);
separator->SetSize(kUrlBarSeparatorWidthDMM, kUrlBarHeightDMM);
VR_BIND_COLOR(model_, separator.get(), &ColorScheme::url_bar_separator,
&Rect::SetColor);
scene_->AddUiElement(kUrlBarLayout, std::move(separator));
if (model_->create_tabs_view) {
auto tabs_button = Create<Button>(
kNone, kPhaseForeground,
base::BindRepeating(
[](Model* model) { model->push_mode(kModeTabsView); },
base::Unretained(model_)),
audio_delegate_);
tabs_button->SetSize(kUrlBarEndButtonWidthDMM, kUrlBarHeightDMM);
tabs_button->set_hover_offset(0);
VR_BIND_BUTTON_COLORS(model_, tabs_button.get(),
&ColorScheme::url_bar_button,
&Button::SetButtonColors);
auto tab_count_text =
Create<Text>(kNone, kPhaseForeground, kUrlBarFontHeightDMM);
tab_count_text->SetLayoutMode(TextLayoutMode::kSingleLine);
tab_count_text->AddBinding(std::make_unique<Binding<size_t>>(
VR_BIND_LAMBDA(
[](Model* model) {
return !model->incognito ? model->regular_tabs.size()
: model->incognito_tabs.size();
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Text* tab_count_text, const size_t& tab_count) {
tab_count_text->SetText(base::NumberToString16(tab_count));
},
base::Unretained(tab_count_text.get()))));
VR_BIND_COLOR(model_, tab_count_text.get(), &ColorScheme::url_bar_text,
&Text::SetColor);
tabs_button->AddChild(std::move(tab_count_text));
scene_->AddUiElement(kUrlBarLayout, std::move(tabs_button));
separator = Create<Rect>(kUrlBarTabSeparator, kPhaseForeground);
separator->set_hit_testable(true);
separator->SetSize(kUrlBarSeparatorWidthDMM, kUrlBarHeightDMM);
VR_BIND_COLOR(model_, separator.get(), &ColorScheme::url_bar_separator,
&Rect::SetColor);
scene_->AddUiElement(kUrlBarLayout, std::move(separator));
}
auto overflow_button = Create<VectorIconButton>(
kUrlBarOverflowButton, kPhaseForeground,
base::BindRepeating(
[](Model* model) { model->overflow_menu_enabled = true; },
base::Unretained(model_)),
kMoreVertIcon, audio_delegate_);
overflow_button->SetSize(kUrlBarEndButtonWidthDMM, kUrlBarHeightDMM);
overflow_button->SetCornerRadii(
{0, kUrlBarHeightDMM / 2, 0, kUrlBarHeightDMM / 2});
overflow_button->set_hover_offset(0);
overflow_button->SetIconScaleFactor(kUrlBarButtonIconSizeDMM /
kUrlBarHeightDMM);
overflow_button->SetIconTranslation(-kUrlBarEndButtonIconOffsetDMM, 0);
VR_BIND_BUTTON_COLORS(model_, overflow_button.get(),
&ColorScheme::url_bar_button, &Button::SetButtonColors);
scene_->AddUiElement(kUrlBarLayout, std::move(overflow_button));
}
void UiSceneCreator::CreateOverflowMenu() {
auto overflow_backplane =
Create<InvisibleHitTarget>(kOverflowMenuBackplane, kPhaseForeground);
EventHandlers event_handlers;
event_handlers.button_up = base::BindRepeating(
[](Model* model) { model->overflow_menu_enabled = false; },
base::Unretained(model_));
overflow_backplane->set_event_handlers(event_handlers);
overflow_backplane->SetSize(kBackplaneSize, kBackplaneSize);
overflow_backplane->set_contributes_to_parent_bounds(false);
overflow_backplane->set_y_anchoring(TOP);
overflow_backplane->SetRotate(1, 0, 0, -kUrlBarRotationRad);
VR_BIND_VISIBILITY(overflow_backplane, model->overflow_menu_enabled);
auto overflow_menu = Create<Rect>(kOverflowMenu, kPhaseForeground);
overflow_menu->set_hit_testable(true);
overflow_menu->set_y_centering(BOTTOM);
overflow_menu->set_bounds_contain_children(true);
overflow_menu->set_contributes_to_parent_bounds(false);
overflow_menu->SetTranslate(0, kOverflowMenuOffset, 0);
overflow_menu->set_corner_radius(kUrlBarItemCornerRadiusDMM);
VR_BIND_COLOR(model_, overflow_menu.get(), &ColorScheme::omnibox_background,
&Rect::SetColor);
auto overflow_outer_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kUp);
auto button_region = Create<UiElement>(kNone, kPhaseNone);
button_region->set_bounds_contain_children(true);
button_region->set_y_anchoring(BOTTOM);
button_region->set_y_centering(BOTTOM);
button_region->set_contributes_to_parent_bounds(false);
auto button_region_bg = Create<Rect>(kNone, kPhaseForeground);
button_region_bg->SetSize(kOverflowMenuMinimumWidth,
kOverflowButtonRegionHeight);
button_region_bg->SetCornerRadii(
{0.0f, 0.0f, kUrlBarItemCornerRadiusDMM, kUrlBarItemCornerRadiusDMM});
button_region_bg->SetOpacity(kOverflowButtonRegionOpacity);
VR_BIND_COLOR(model_, button_region_bg.get(),
&ColorScheme::omnibox_background, &Rect::SetColor);
button_region->AddChild(std::move(button_region_bg));
// The forward and refresh buttons are anchored to the bottom corners of the
// reserved space. In the future, when we have more buttons, they may instead
// be placed in a linear layout (locked to one side).
std::vector<
std::tuple<UiElementName, LayoutAlignment, const gfx::VectorIcon&>>
menu_buttons = {
{kOverflowMenuForwardButton, LEFT, vector_icons::kForwardArrowIcon},
{kOverflowMenuReloadButton, RIGHT, vector_icons::kReloadIcon},
};
for (auto& item : menu_buttons) {
auto button = Create<VectorIconButton>(std::get<0>(item), kPhaseForeground,
base::DoNothing(), std::get<2>(item),
audio_delegate_);
button->SetType(kTypeOverflowMenuButton);
button->SetDrawPhase(kPhaseForeground);
button->SetSize(kUrlBarButtonSizeDMM, kUrlBarButtonSizeDMM);
button->SetIconScaleFactor(kUrlBarButtonIconScaleFactor);
button->set_hover_offset(kUrlBarButtonHoverOffsetDMM);
button->set_corner_radius(kUrlBarItemCornerRadiusDMM);
button->set_requires_layout(false);
button->set_contributes_to_parent_bounds(false);
button->set_x_anchoring(std::get<1>(item));
button->set_x_centering(std::get<1>(item));
button->set_y_anchoring(BOTTOM);
button->set_y_centering(BOTTOM);
button->SetTranslate(
kOverflowButtonXPadding * (std::get<1>(item) == RIGHT ? -1 : 1),
kOverflowMenuYPadding, 0);
VR_BIND_BUTTON_COLORS(model_, button.get(), &ColorScheme::url_bar_button,
&Button::SetButtonColors);
switch (std::get<0>(item)) {
case kOverflowMenuForwardButton:
button->set_click_handler(base::BindRepeating(
[](Model* model, UiBrowserInterface* browser) {
model->overflow_menu_enabled = false;
browser->NavigateForward();
},
base::Unretained(model_), base::Unretained(browser_)));
button->AddBinding(VR_BIND_FUNC(bool, Model, model_,
model->can_navigate_forward, Button,
button.get(), SetEnabled));
break;
case kOverflowMenuReloadButton:
button->set_click_handler(base::BindRepeating(
[](Model* model, UiBrowserInterface* browser) {
model->overflow_menu_enabled = false;
browser->ReloadTab();
},
base::Unretained(model_), base::Unretained(browser_)));
break;
default:
break;
}
button_region->AddChild(std::move(button));
}
struct MenuItem {
UiElementName name;
int string_id;
base::RepeatingCallback<void(UiBrowserInterface*)> action;
base::RepeatingCallback<bool(Model*)> visibility;
};
int new_incognito_tab_res_id = IDS_VR_MENU_NEW_INCOGNITO_TAB;
int close_incognito_tabs_res_id = IDS_VR_MENU_CLOSE_INCOGNITO_TABS;
#if defined(OS_ANDROID)
if (base::FeatureList::IsEnabled(features::kIncognitoStrings)) {
new_incognito_tab_res_id = IDS_VR_MENU_NEW_PRIVATE_TAB;
close_incognito_tabs_res_id = IDS_VR_MENU_CLOSE_PRIVATE_TABS;
}
#endif // defined(OS_ANDROID)
std::vector<MenuItem> menu_items = {
{
kOverflowMenuNewIncognitoTabItem, new_incognito_tab_res_id,
base::BindRepeating(
[](UiBrowserInterface* browser) { browser->OpenNewTab(true); }),
base::BindRepeating([](Model* m) { return !m->incognito; }),
},
{kOverflowMenuCloseAllIncognitoTabsItem, close_incognito_tabs_res_id,
base::BindRepeating([](UiBrowserInterface* browser) {
browser->CloseAllIncognitoTabs();
}),
base::BindRepeating(
[](Model* m) { return !m->incognito_tabs.empty(); })},
{kOverflowMenuPreferencesItem, IDS_VR_MENU_PREFERENCES,
base::BindRepeating(
[](UiBrowserInterface* browser) { browser->OpenSettings(); }),
base::BindRepeating([](Model* m) { return m->standalone_vr_device; })},
};
auto overflow_menu_scroll = Create<ScrollableElement>(
kNone, kPhaseNone, ScrollableElement::kVertical);
overflow_menu_scroll->set_max_span(kOverflowMenuMaxSpan);
overflow_menu_scroll->SetScrollAnchoring(TOP);
auto overflow_menu_layout = Create<LinearLayout>(
kOverflowMenuLayout, kPhaseNone, LinearLayout::kDown);
for (auto& item : menu_items) {
auto layout = std::make_unique<LinearLayout>(LinearLayout::kRight);
layout->SetType(kTypeOverflowMenuItem);
layout->SetDrawPhase(kPhaseNone);
auto text =
Create<Text>(kNone, kPhaseForeground, kSuggestionContentTextHeightDMM);
text->SetDrawPhase(kPhaseForeground);
text->SetText(l10n_util::GetStringUTF16(item.string_id));
text->SetLayoutMode(TextLayoutMode::kSingleLineFixedWidth);
text->SetFieldWidth(kOverflowMenuMinimumWidth -
2 * kOverflowMenuItemXPadding);
text->SetAlignment(kTextAlignmentLeft);
VR_BIND_COLOR(model_, text.get(), &ColorScheme::menu_text, &Text::SetColor);
layout->AddChild(std::move(text));
auto spacer = Create<Rect>(kNone, kPhaseNone);
spacer->SetType(kTypeSpacer);
spacer->SetSize(0, kOverflowMenuItemHeight);
layout->AddChild(std::move(spacer));
auto background = Create<Button>(item.name, kPhaseForeground,
base::DoNothing(), audio_delegate_);
background->set_hit_testable(true);
background->set_bounds_contain_children(true);
background->set_hover_offset(0);
background->set_padding(kOverflowMenuItemXPadding, 0);
VR_BIND_BUTTON_COLORS(model_, background.get(),
&ColorScheme::url_bar_button,
&Button::SetButtonColors);
background->AddChild(std::move(layout));
base::RepeatingClosure callback =
base::BindRepeating(item.action, base::Unretained(browser_));
background->set_click_handler(base::BindRepeating(
[](Model* model, const base::RepeatingClosure& callback) {
model->overflow_menu_enabled = false;
callback.Run();
},
base::Unretained(model_), callback));
background->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(item.visibility, base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, const bool& value) { e->SetVisible(value); },
base::Unretained(background.get()))));
overflow_menu_layout->AddChild(std::move(background));
}
// The item that reserves space in the menu layout for the buttons.
auto button_spacer =
CreateSpacer(kOverflowMenuMinimumWidth, kOverflowButtonRegionHeight);
overflow_menu_layout->AddChild(std::move(button_spacer));
overflow_menu_scroll->AddScrollingChild(std::move(overflow_menu_layout));
overflow_outer_layout->AddChild(std::move(overflow_menu_scroll));
auto top_cap = Create<Rect>(kNone, kPhaseNone);
top_cap->SetType(kTypeSpacer);
top_cap->SetSize(kOverflowMenuMinimumWidth, kOverflowMenuYPadding);
overflow_outer_layout->AddChild(std::move(top_cap));
overflow_menu->AddChild(std::move(overflow_outer_layout));
overflow_menu->AddChild(std::move(button_region));
overflow_backplane->AddChild(std::move(overflow_menu));
scene_->AddUiElement(kUrlBarOverflowButton, std::move(overflow_backplane));
}
void UiSceneCreator::CreateOmnibox() {
auto scaler =
Create<ScaledDepthAdjuster>(kOmniboxDmmRoot, kPhaseNone, kUrlBarDistance);
auto omnibox_root = Create<UiElement>(kOmniboxRoot, kPhaseNone);
omnibox_root->SetVisible(false);
omnibox_root->SetTransitionedProperties({OPACITY});
omnibox_root->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kOmniboxTransitionMs));
VR_BIND_VISIBILITY(omnibox_root, model->get_mode() == kModeEditingOmnibox);
auto omnibox_outer_layout =
Create<LinearLayout>(kOmniboxOuterLayout, kPhaseNone, LinearLayout::kUp);
auto omnibox_suggestion_divider = Create<Rect>(kNone, kPhaseForeground);
omnibox_suggestion_divider->SetType(kTypeSpacer);
omnibox_suggestion_divider->SetSize(kOmniboxWidthDMM, kSuggestionGapDMM);
VR_BIND_COLOR(model_, omnibox_suggestion_divider.get(),
&ColorScheme::url_bar_separator, &Rect::SetColor);
auto omnibox_text_field = Create<OmniboxTextField>(
kOmniboxTextField, kPhaseNone, kOmniboxTextHeightDMM,
base::BindRepeating(
[](Model* model, const EditedText& text_input_info) {
model->omnibox_text_field_info = text_input_info;
},
base::Unretained(model_)),
base::BindRepeating(
[](UiBrowserInterface* browser, const AutocompleteRequest& request) {
browser->StartAutocomplete(request);
},
base::Unretained(browser_)),
base::BindRepeating(
[](UiBrowserInterface* browser) { browser->StopAutocomplete(); },
base::Unretained(browser_)));
omnibox_text_field->SetTextInputDelegate(text_input_delegate_);
omnibox_text_field->set_hit_testable(false);
omnibox_text_field->SetHintText(
l10n_util::GetStringUTF16(IDS_SEARCH_OR_TYPE_WEB_ADDRESS));
// TODO(crbug.com/834308): Refactor this element to be resized by a
// fixed-width layout, rather than adjusting based on other elements.
omnibox_text_field->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* m) {
return m->speech.has_or_can_request_audio_permission &&
!m->incognito && !m->active_capturing.audio_capture_enabled;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](TextInput* e, const bool& mic_button_visible) {
float width = kOmniboxWidthDMM - 2 * kOmniboxTextMarginDMM;
if (mic_button_visible) {
width -= kOmniboxTextFieldIconButtonSizeDMM +
kOmniboxTextFieldRightMargin;
}
e->SetSize(width, 0);
},
base::Unretained(omnibox_text_field.get()))));
EventHandlers event_handlers;
event_handlers.focus_change = base::BindRepeating(
[](Model* model, TextInput* text_input, bool focused) {
if (focused) {
model->editing_input = true;
text_input->UpdateInput(model->omnibox_text_field_info);
} else {
model->editing_input = false;
model->pop_mode(kModeEditingOmnibox);
}
},
base::Unretained(model_), base::Unretained(omnibox_text_field.get()));
omnibox_text_field->set_event_handlers(event_handlers);
omnibox_text_field->AddBinding(VR_BIND_FUNC(
bool, Model, model_, model->has_mode_in_stack(kModeEditingOmnibox),
OmniboxTextField, omnibox_text_field.get(), SetEnabled));
omnibox_text_field->AddBinding(std::make_unique<Binding<EditedText>>(
VR_BIND_LAMBDA(
[](Model* model) { return model->omnibox_text_field_info; },
base::Unretained(model_)),
VR_BIND_LAMBDA([](OmniboxTextField* e,
const EditedText& value) { e->UpdateInput(value); },
base::Unretained(omnibox_text_field.get()))));
omnibox_text_field->set_input_committed_callback(base::BindRepeating(
[](Model* model, UiBrowserInterface* browser, Ui* ui,
const EditedText& text) {
if (!model->omnibox_suggestions.empty()) {
browser->Navigate(model->omnibox_suggestions.front().destination,
NavigationMethod::kOmniboxUrlEntry);
ui->OnUiRequestedNavigation();
}
},
base::Unretained(model_), base::Unretained(browser_),
base::Unretained(ui_)));
omnibox_text_field->AddBinding(std::make_unique<Binding<Autocompletion>>(
VR_BIND_LAMBDA(
[](Model* m) {
if (!m->omnibox_suggestions.empty()) {
return m->omnibox_suggestions.front().autocompletion;
} else {
return Autocompletion();
}
},
base::Unretained(model_)),
VR_BIND_LAMBDA([](OmniboxTextField* e,
const Autocompletion& v) { e->SetAutocompletion(v); },
base::Unretained(omnibox_text_field.get()))));
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(VR_BIND_FUNC(
bool, Model, model_, model->supports_selection, OmniboxTextField,
omnibox_text_field.get(), set_allow_inline_autocomplete));
VR_BIND_COLOR(model_, omnibox_text_field.get(), &ColorScheme::url_bar_text,
&TextInput::SetTextColor);
VR_BIND_COLOR(model_, omnibox_text_field.get(),
&ColorScheme::url_bar_hint_text, &TextInput::SetHintColor);
omnibox_text_field->AddBinding(std::make_unique<Binding<TextSelectionColors>>(
VR_BIND_LAMBDA(
[](Model* m) { return m->color_scheme().omnibox_text_selection; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](TextInput* e, const TextSelectionColors& colors) {
e->SetSelectionColors(colors);
},
base::Unretained(omnibox_text_field.get()))));
auto mic_button = Create<VectorIconButton>(
kOmniboxVoiceSearchButton, kPhaseForeground,
base::BindRepeating(
[](UiBrowserInterface* b, Ui* ui) { b->SetVoiceSearchActive(true); },
base::Unretained(browser_), base::Unretained(ui_)),
vector_icons::kMicIcon, audio_delegate_);
mic_button->SetSize(kUrlBarButtonSizeDMM, kUrlBarButtonSizeDMM);
mic_button->SetIconScaleFactor(kUrlBarButtonIconScaleFactor);
mic_button->set_hover_offset(kUrlBarButtonHoverOffsetDMM);
mic_button->set_corner_radius(kUrlBarItemCornerRadiusDMM);
VR_BIND_VISIBILITY(mic_button,
model->speech.has_or_can_request_audio_permission &&
!model->incognito &&
!model->active_capturing.audio_capture_enabled);
VR_BIND_BUTTON_COLORS(model_, mic_button.get(), &ColorScheme::url_bar_button,
&Button::SetButtonColors);
auto mic_button_spacer =
CreateSpacer(kOmniboxMicIconRightMarginDMM, kOmniboxHeightDMM);
VR_BIND_VISIBILITY(mic_button_spacer,
model->speech.has_or_can_request_audio_permission &&
!model->incognito &&
!model->active_capturing.audio_capture_enabled);
auto text_field_layout = Create<LinearLayout>(
kOmniboxTextFieldLayout, kPhaseNone, LinearLayout::kRight);
text_field_layout->AddChild(
CreateSpacer(kOmniboxTextMarginDMM, kOmniboxHeightDMM));
text_field_layout->AddChild(std::move(omnibox_text_field));
text_field_layout->AddChild(
CreateSpacer(kOmniboxTextMarginDMM, kOmniboxHeightDMM));
text_field_layout->AddChild(std::move(mic_button));
text_field_layout->AddChild(std::move(mic_button_spacer));
// 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_), base::Unretained(audio_delegate_));
SuggestionSetBinding::ModelRemovedCallback removed_callback =
base::BindRepeating(&OnSuggestionModelRemoved, base::Unretained(scene_));
auto suggestions_layout =
Create<LinearLayout>(kOmniboxSuggestions, kPhaseNone, LinearLayout::kUp);
suggestions_layout->AddBinding(std::make_unique<SuggestionSetBinding>(
&model_->omnibox_suggestions, added_callback, removed_callback));
auto button_scaler = Create<ScaledDepthAdjuster>(
kNone, kPhaseNone, kOmniboxCloseButtonDepthOffset);
auto close_button = Create<DiscButton>(
kOmniboxCloseButton, kPhaseForeground,
base::BindRepeating(
[](Model* model) { model->pop_mode(kModeEditingOmnibox); },
base::Unretained(model_)),
vector_icons::kBackArrowIcon, audio_delegate_);
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::disc_button_colors,
&DiscButton::SetButtonColors);
auto suggestions_outer_layout = Create<LinearLayout>(
kOmniboxSuggestionsOuterLayout, kPhaseNone, LinearLayout::kUp);
VR_BIND_VISIBILITY(suggestions_outer_layout,
!model->omnibox_suggestions.empty());
suggestions_outer_layout->AddChild(std::move(omnibox_suggestion_divider));
suggestions_outer_layout->AddChild(
CreateSpacer(kOmniboxWidthDMM, kSuggestionVerticalPaddingDMM));
suggestions_outer_layout->AddChild(std::move(suggestions_layout));
suggestions_outer_layout->AddChild(
CreateSpacer(kOmniboxWidthDMM, kSuggestionVerticalPaddingDMM));
omnibox_outer_layout->AddChild(std::move(text_field_layout));
omnibox_outer_layout->AddChild(std::move(suggestions_outer_layout));
// Rounded-corner background of all omnibox and suggestion elements.
auto omnibox_background = Create<Rect>(kOmniboxBackground, kPhaseForeground);
omnibox_background->set_bounds_contain_children(true);
omnibox_background->set_hit_testable(true);
omnibox_background->set_y_centering(BOTTOM);
omnibox_background->set_contributes_to_parent_bounds(false);
omnibox_background->set_focusable(false);
omnibox_background->set_corner_radius(kOmniboxCornerRadiusDMM);
omnibox_background->SetTranslate(
0, kOmniboxVerticalOffsetDMM - 0.5 * kOmniboxHeightDMM, 0);
VR_BIND_COLOR(model_, omnibox_background.get(),
&ColorScheme::omnibox_background, &Rect::SetColor);
omnibox_background->AddChild(std::move(omnibox_outer_layout));
button_scaler->AddChild(std::move(close_button));
omnibox_root->AddChild(std::move(omnibox_background));
omnibox_root->AddChild(std::move(button_scaler));
scaler->AddChild(std::move(omnibox_root));
UiElement* parent = scene_->GetUiElementByName(k2dBrowsingRepositioner);
parent->AddChild(std::move(scaler));
// This binding must run whether or not the omnibox is visible.
parent->AddBinding(std::make_unique<Binding<std::pair<bool, base::string16>>>(
VR_BIND_LAMBDA(
[](Model* m) {
bool editing_omnibox = m->has_mode_in_stack(kModeEditingOmnibox);
base::string16 url_text =
FormatUrlForVr(m->toolbar_state.gurl, nullptr);
return std::make_pair(editing_omnibox, url_text);
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Model* m, const std::pair<bool, base::string16>& value) {
if (value.first /* editing_omnibox */) {
m->omnibox_text_field_info.current =
TextInputInfo(value.second, 0, value.second.size());
} else {
m->omnibox_text_field_info = EditedText();
}
},
base::Unretained(model_))));
}
void UiSceneCreator::CreateCloseButton() {
base::RepeatingCallback<void()> click_handler = base::BindRepeating(
[](Model* model, UiBrowserInterface* browser) {
if (model->fullscreen_enabled()) {
browser->ExitFullscreen();
}
},
base::Unretained(model_), base::Unretained(browser_));
std::unique_ptr<DiscButton> element =
Create<DiscButton>(kCloseButton, kPhaseForeground, click_handler,
vector_icons::kCloseRoundedIcon, audio_delegate_);
element->set_contributes_to_parent_bounds(false);
element->SetSize(kCloseButtonDiameter, kCloseButtonDiameter);
element->set_hover_offset(kButtonZOffsetHoverDMM * kCloseButtonDistance);
element->set_y_anchoring(BOTTOM);
element->SetTranslate(0, kCloseButtonRelativeOffset, -kCloseButtonDistance);
VR_BIND_BUTTON_COLORS(model_, element.get(), &ColorScheme::disc_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());
element->AddBinding(
VR_BIND(bool, Model, model_, model->fullscreen_enabled(), UiElement,
element.get(),
view->SetTranslate(0, kCloseButtonRelativeOffset,
value ? -kCloseButtonFullscreenDistance
: -kCloseButtonDistance)));
element->AddBinding(VR_BIND(
bool, Model, model_, model->fullscreen_enabled(), UiElement,
element.get(),
view->SetRotate(
1, 0, 0,
atan(value ? kCloseButtonFullscreenVerticalOffset /
kCloseButtonFullscreenDistance
: kCloseButtonVerticalOffset / 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::CreatePrompts() {
auto prompt = CreatePrompt(model_);
prompt->SetName(kExitPrompt);
VR_BIND_VISIBILITY(prompt,
model->active_modal_prompt_type != kModalPromptTypeNone);
prompt->AddBinding(std::make_unique<Binding<ModalPromptType>>(
VR_BIND_LAMBDA([](Model* m) { return m->active_modal_prompt_type; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](UiElement* e, Model* model, UiBrowserInterface* browser,
const ModalPromptType& type) {
if (type == kModalPromptTypeNone)
return;
int message_id = 0;
const gfx::VectorIcon* icon = nullptr;
int primary_button_text_id = 0;
int secondary_button_text_id = 0;
ExitVrPromptChoice primary_choice = CHOICE_EXIT;
ExitVrPromptChoice secondary_choice = CHOICE_STAY;
UiUnsupportedMode reason = GetReasonForPrompt(type);
switch (type) {
case kModalPromptTypeExitVRForVoiceSearchRecordAudioOsPermission:
message_id = IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_DESCRIPTION;
icon = &vector_icons::kMicIcon;
primary_button_text_id =
IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_CONTINUE_BUTTON;
secondary_button_text_id =
IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_ABORT_BUTTON;
break;
case kModalPromptTypeUpdateKeyboard:
message_id = IDS_VR_UPDATE_KEYBOARD_PROMPT;
icon = &vector_icons::kInfoOutlineIcon;
primary_button_text_id =
IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_CONTINUE_BUTTON;
secondary_button_text_id =
IDS_VR_SHELL_AUDIO_PERMISSION_PROMPT_ABORT_BUTTON;
break;
case kModalPromptTypeExitVRForSiteInfo:
message_id = IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION_SITE_INFO;
icon = &vector_icons::kInfoOutlineIcon;
primary_button_text_id =
IDS_VR_SHELL_EXIT_PROMPT_EXIT_VR_BUTTON;
secondary_button_text_id = IDS_VR_BUTTON_BACK;
break;
case kModalPromptTypeExitVRForCertificateInfo:
case kModalPromptTypeExitVRForConnectionSecurityInfo:
case kModalPromptTypeGenericUnsupportedFeature:
message_id = IDS_VR_SHELL_EXIT_PROMPT_DESCRIPTION;
icon = &vector_icons::kInfoOutlineIcon;
primary_button_text_id =
IDS_VR_SHELL_EXIT_PROMPT_EXIT_VR_BUTTON;
secondary_button_text_id = IDS_VR_BUTTON_BACK;
break;
case kNumModalPromptTypes:
case kModalPromptTypeNone:
NOTREACHED();
break;
}
Text* text_element =
static_cast<Text*>(e->GetDescendantByType(kTypePromptText));
text_element->SetText(l10n_util::GetStringUTF16(message_id));
VectorIcon* icon_element = static_cast<VectorIcon*>(
e->GetDescendantByType(kTypePromptIcon));
icon_element->SetIcon(icon);
TextButton* primary_button = static_cast<TextButton*>(
e->GetDescendantByType(kTypePromptPrimaryButton));
// TODO(crbug.com/787654): Uppercasing should be conditional.
primary_button->SetText(base::i18n::ToUpper(
l10n_util::GetStringUTF16(primary_button_text_id)));
primary_button->set_click_handler(
CreatePromptCallback(reason, primary_choice, model, browser));
TextButton* secondary_button = static_cast<TextButton*>(
e->GetDescendantByType(kTypePromptSecondaryButton));
// TODO(crbug.com/787654): Uppercasing should be conditional.
secondary_button->SetText(base::i18n::ToUpper(
l10n_util::GetStringUTF16(secondary_button_text_id)));
secondary_button->set_click_handler(
CreatePromptCallback(reason, secondary_choice, model, browser));
InvisibleHitTarget* backplane = static_cast<InvisibleHitTarget*>(
e->GetDescendantByType(kTypePromptBackplane));
EventHandlers event_handlers;
event_handlers.button_up =
CreatePromptCallback(reason, CHOICE_NONE, model, browser);
backplane->set_event_handlers(event_handlers);
},
base::Unretained(prompt.get()), base::Unretained(model_),
base::Unretained(browser_))));
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(prompt));
}
void UiSceneCreator::CreateWebVrOverlayElements() {
// Create transient WebVR elements.
auto indicators = Create<LinearLayout>(kWebVrIndicatorLayout, kPhaseNone,
LinearLayout::kDown);
indicators->SetTranslate(0, 0, kWebVrPermissionDepth);
indicators->set_margin(kWebVrPermissionOuterMargin);
IndicatorSpec app_button_spec = {kNone,
kWebVrExclusiveScreenToast,
kRemoveCircleOutlineIcon,
IDS_PRESS_APP_TO_EXIT,
0,
0,
nullptr,
false};
indicators->AddChild(CreateWebVrIndicator(model_, browser_, app_button_spec));
auto specs = GetIndicatorSpecs();
for (const auto& spec : specs) {
indicators->AddChild(CreateWebVrIndicator(model_, browser_, spec));
}
auto parent = CreateTransientParent(kWebVrIndicatorTransience,
kToastTimeoutSeconds, true);
parent->AddBinding(std::make_unique<Binding<std::tuple<bool, bool, bool>>>(
VR_BIND_LAMBDA(
[](Model* model) {
return std::tuple<bool, bool, bool>(
model->web_vr_enabled() && model->web_vr.presenting_web_vr() &&
model->web_vr.has_received_permissions,
model->menu_button_long_pressed,
model->web_vr.showing_hosted_ui);
},
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](TransientElement* e, Model* model, UiScene* scene,
const base::Optional<std::tuple<bool, bool, bool>>& last_value,
const std::tuple<bool, bool, bool>& value) {
const bool in_web_vr_presentation = std::get<0>(value);
const bool in_long_press = std::get<1>(value);
const bool showing_hosted_ui = std::get<2>(value);
const bool was_in_long_press =
last_value && std::get<1>(last_value.value());
const bool was_showing_hosted_ui =
last_value && std::get<2>(last_value.value());
if (!in_web_vr_presentation) {
e->SetVisibleImmediately(false);
return;
}
// The reason we need the previous state is to disguish the
// situation where the app button has been released after a long
// press, and the situation when we want to initially show the
// indicators.
if (was_in_long_press && !in_long_press)
return;
// Similarly, we need to know when we've finished presenting hosted
// ui because we should not show indicators then.
if (was_showing_hosted_ui && !showing_hosted_ui)
return;
e->SetVisible(true);
e->RefreshVisible();
SetVisibleInLayout(
scene->GetUiElementByName(kWebVrExclusiveScreenToast),
!model->browsing_disabled && !in_long_press);
auto specs = GetIndicatorSpecs();
for (const auto& spec : specs) {
SetVisibleInLayout(
scene->GetUiElementByName(spec.webvr_name),
model->active_capturing.*spec.signal ||
model->potential_capturing.*spec.signal ||
model->background_capturing.*spec.signal);
}
e->RemoveKeyframeModels(TRANSFORM);
if (in_long_press) {
// We do not do a translation animation for long press.
e->SetTranslate(0, 0, 0);
return;
}
e->SetTranslate(0, kWebVrPermissionOffsetStart, 0);
// Build up a keyframe model for the initial transition.
std::unique_ptr<cc::KeyframedTransformAnimationCurve> curve(
cc::KeyframedTransformAnimationCurve::Create());
cc::TransformOperations value_1;
value_1.AppendTranslate(0, kWebVrPermissionOffsetStart, 0);
curve->AddKeyframe(cc::TransformKeyframe::Create(
base::TimeDelta(), value_1,
cc::CubicBezierTimingFunction::CreatePreset(
cc::CubicBezierTimingFunction::EaseType::EASE)));
cc::TransformOperations value_2;
value_2.AppendTranslate(0, kWebVrPermissionOffsetOvershoot, 0);
curve->AddKeyframe(cc::TransformKeyframe::Create(
base::TimeDelta::FromMilliseconds(kWebVrPermissionOffsetMs),
value_2,
cc::CubicBezierTimingFunction::CreatePreset(
cc::CubicBezierTimingFunction::EaseType::EASE)));
cc::TransformOperations value_3;
value_3.AppendTranslate(0, kWebVrPermissionOffsetFinal, 0);
curve->AddKeyframe(cc::TransformKeyframe::Create(
base::TimeDelta::FromMilliseconds(
kWebVrPermissionAnimationDurationMs),
value_3,
cc::CubicBezierTimingFunction::CreatePreset(
cc::CubicBezierTimingFunction::EaseType::EASE)));
e->AddKeyframeModel(cc::KeyframeModel::Create(
std::move(curve), Animation::GetNextKeyframeModelId(),
Animation::GetNextGroupId(), TRANSFORM));
},
base::Unretained(parent.get()), base::Unretained(model_),
base::Unretained(scene_))));
auto scaler = std::make_unique<ScaledDepthAdjuster>(kWebVrToastDistance);
scaler->AddChild(std::move(indicators));
parent->AddChild(std::move(scaler));
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(parent));
}
void UiSceneCreator::CreateToasts() {
auto platform_toast = CreateTextToast(
kPlatformToastTransientParent, kPlatformToast, model_, base::string16());
platform_toast->set_contributes_to_parent_bounds(false);
platform_toast->set_y_anchoring(BOTTOM);
platform_toast->set_y_centering(TOP);
platform_toast->SetTranslate(0, kPlatformToastVerticalOffset,
kIndicatorDistanceOffset);
platform_toast->AddBinding(std::make_unique<Binding<const PlatformToast*>>(
VR_BIND_LAMBDA([](Model* m) { return m->platform_toast.get(); },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](TransientElement* t, const PlatformToast* const& value) {
t->SetVisible(value);
if (value) {
t->RefreshVisible();
}
},
base::Unretained(platform_toast.get()))));
Text* text_element =
static_cast<Text*>(platform_toast->GetDescendantByType(kTypeToastText));
DCHECK(text_element);
text_element->AddBinding(std::make_unique<Binding<const PlatformToast*>>(
VR_BIND_LAMBDA([](Model* m) { return m->platform_toast.get(); },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Text* t, const PlatformToast* const& value) {
if (value) {
t->SetText(value->text);
}
},
base::Unretained(text_element))));
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(platform_toast));
}
void UiSceneCreator::CreateTabsViews() {
if (!model_->create_tabs_view) {
return;
}
auto scaler =
Create<ScaledDepthAdjuster>(kNone, kPhaseNone, kTabsViewDistance);
auto tabs_view_root =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kDown);
tabs_view_root->SetTranslate(0, kTabsViewVerticalOffsetDMM, 0);
tabs_view_root->set_bounds_contain_children(true);
tabs_view_root->set_margin(kTabsViewRootMarginDMM);
tabs_view_root->SetVisible(false);
VR_BIND_VISIBILITY(tabs_view_root,
model->get_last_opaque_mode() == kModeTabsView);
auto regular_tabs_view =
CreateTabsView(model_, scene_, audio_delegate_, browser_, false);
VR_BIND_VISIBILITY(regular_tabs_view, !model->incognito_tabs_view_selected);
tabs_view_root->AddChild(std::move(regular_tabs_view));
auto incognito_tabs_view =
CreateTabsView(model_, scene_, audio_delegate_, browser_, true);
VR_BIND_VISIBILITY(incognito_tabs_view, model->incognito_tabs_view_selected);
tabs_view_root->AddChild(std::move(incognito_tabs_view));
auto mode_switcher_layout =
Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kRight);
mode_switcher_layout->set_bounds_contain_children(true);
mode_switcher_layout->AddChild(
CreateTabModeButton(model_, audio_delegate_, false));
mode_switcher_layout->AddChild(
CreateTabModeButton(model_, audio_delegate_, true));
tabs_view_root->AddChild(std::move(mode_switcher_layout));
auto button_scaler = Create<ScaledDepthAdjuster>(
kNone, kPhaseNone, kTabsViewCloseButtonDepthOffset);
auto close_button = Create<DiscButton>(
kNone, kPhaseForeground,
base::BindRepeating([](Model* model) { model->pop_mode(kModeTabsView); },
base::Unretained(model_)),
vector_icons::kBackArrowIcon, audio_delegate_);
close_button->SetSize(kButtonDiameterDMM, kButtonDiameterDMM);
close_button->SetTranslate(0, kTabsViewCloseButtonVerticalOffsetDMM, 0);
close_button->SetRotate(1, 0, 0, atan(kTabsViewCloseButtonVerticalOffsetDMM));
close_button->set_hover_offset(kButtonZOffsetHoverDMM);
VR_BIND_BUTTON_COLORS(model_, close_button.get(),
&ColorScheme::disc_button_colors,
&DiscButton::SetButtonColors);
button_scaler->AddChild(std::move(close_button));
tabs_view_root->AddChild(std::move(button_scaler));
scaler->AddChild(std::move(tabs_view_root));
scene_->AddUiElement(k2dBrowsingRepositioner, std::move(scaler));
}
} // namespace vr