| // 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 <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #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/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/app/vector_icons/vector_icons.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/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/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/grit/generated_resources.h" |
| #include "components/omnibox/browser/vector_icons.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "components/url_formatter/elide_url.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/gfx/paint_vector_icon.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(const gfx::VectorIcon*, SuggestionBinding, element_binding, |
| model->model()->icon, VectorIcon, p_icon, view->SetIcon(*value))); |
| element_binding->set_view(background.get()); |
| scene->AddUiElement(kOmniboxSuggestions, std::move(background)); |
| } |
| |
| void OnSuggestionModelRemoved(UiScene* scene, SuggestionBinding* binding) { |
| scene->RemoveUiElement(binding->view()->id()); |
| } |
| |
| std::unique_ptr<TransientElement> CreateTransientParent(UiElementName name, |
| int timeout_seconds, |
| bool animate_opacity) { |
| auto element = std::make_unique<SimpleTransientElement>( |
| base::TimeDelta::FromSeconds(timeout_seconds)); |
| element->SetName(name); |
| element->SetVisible(false); |
| 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)); |
| } |
| |
| typedef VectorBinding<ControllerModel, Controller> ControllerSetBinding; |
| typedef typename ControllerSetBinding::ElementBinding ControllerBinding; |
| |
| std::unique_ptr<UiElement> CreateControllerLabel( |
| UiElementName name, |
| float z_offset, |
| const base::string16& text, |
| Model* model, |
| ControllerBinding* element_binding) { |
| 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, ControllerBinding, element_binding, |
| model->model()->handedness == ControllerModel::kRightHanded ? LEFT |
| : RIGHT, |
| LinearLayout, layout.get(), set_x_centering)); |
| layout->AddBinding( |
| VR_BIND_FUNC(LinearLayout::Direction, ControllerBinding, element_binding, |
| model->model()->handedness == ControllerModel::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, |
| ControllerBinding* element_binding) { |
| auto controller = Create<Controller>(kController, kPhaseForeground); |
| controller->AddBinding(VR_BIND_FUNC(gfx::Transform, ControllerBinding, |
| element_binding, |
| model->model()->transform, Controller, |
| controller.get(), set_local_transform)); |
| controller->AddBinding(VR_BIND_FUNC(float, ControllerBinding, element_binding, |
| model->model()->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(std::make_unique<Binding<SkColor>>( |
| VR_BIND_LAMBDA( |
| [](ControllerBinding* m, Model* model) { |
| return m->model()->touchpad_button_state == |
| ControllerModel::ButtonState::kDown |
| ? model->color_scheme().controller_button_down |
| : model->color_scheme().controller_button; |
| }, |
| base::Unretained(element_binding), base::Unretained(model)), |
| VR_BIND_LAMBDA( |
| [](Rect* touchpad_button, const SkColor& value) { |
| touchpad_button->SetColor(value); |
| }, |
| base::Unretained(touchpad_button.get())))); |
| |
| 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(std::make_unique<Binding<SkColor>>( |
| VR_BIND_LAMBDA( |
| [](ControllerBinding* m, Model* model) { |
| return m->model()->app_button_state == |
| ControllerModel::ButtonState::kDown |
| ? model->color_scheme().controller_button_down |
| : model->color_scheme().controller_button; |
| }, |
| base::Unretained(element_binding), base::Unretained(model)), |
| VR_BIND_LAMBDA([](VectorIcon* app_button, |
| const SkColor& value) { app_button->SetColor(value); }, |
| base::Unretained(app_button.get())))); |
| |
| 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(std::make_unique<Binding<SkColor>>( |
| VR_BIND_LAMBDA( |
| [](ControllerBinding* m, Model* model) { |
| return m->model()->home_button_state == |
| ControllerModel::ButtonState::kDown |
| ? model->color_scheme().controller_button_down |
| : model->color_scheme().controller_button; |
| }, |
| base::Unretained(element_binding), base::Unretained(model)), |
| VR_BIND_LAMBDA([](VectorIcon* home_button, |
| const SkColor& value) { home_button->SetColor(value); }, |
| base::Unretained(home_button.get())))); |
| |
| 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( |
| [](ControllerBinding* m, Model* model, int index) { |
| return m->model()->battery_level > index |
| ? model->color_scheme().controller_battery_full |
| : model->color_scheme().controller_battery_empty; |
| }, |
| base::Unretained(element_binding), 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)); |
| |
| element_binding->set_view(controller.get()); |
| |
| return controller; |
| } |
| |
| void OnControllerModelAdded(UiScene* scene, |
| Model* model, |
| ControllerBinding* element_binding) { |
| auto controller = CreateControllerElement(model, element_binding); |
| |
| auto callout_group = Create<UiElement>(kNone, kPhaseNone); |
| callout_group->SetVisible(false); |
| callout_group->SetTransitionedProperties({OPACITY}); |
| callout_group->SetTransitionDuration( |
| base::TimeDelta::FromMilliseconds(kControllerLabelTransitionDurationMs)); |
| callout_group->AddBinding( |
| VR_BIND_FUNC(bool, ControllerBinding, element_binding, |
| model->model()->resting_in_viewport, UiElement, |
| callout_group.get(), SetVisible)); |
| |
| auto trackpad_button = |
| CreateControllerLabel(kControllerTrackpadLabel, kControllerTrackpadOffset, |
| l10n_util::GetStringUTF16(IDS_VR_BUTTON_TRACKPAD), |
| model, element_binding); |
| trackpad_button->AddBinding( |
| VR_BIND_FUNC(bool, Model, model, !model->reposition_window_enabled(), |
| UiElement, trackpad_button.get(), SetVisible)); |
| callout_group->AddChild(std::move(trackpad_button)); |
| |
| auto reposition_button = CreateControllerLabel( |
| kControllerTrackpadRepositionLabel, kControllerTrackpadOffset, |
| l10n_util::GetStringUTF16(IDS_VR_BUTTON_TRACKPAD_REPOSITION), model, |
| element_binding); |
| reposition_button->AddBinding( |
| VR_BIND_FUNC(bool, Model, model, model->reposition_window_enabled(), |
| UiElement, reposition_button.get(), SetVisible)); |
| callout_group->AddChild(std::move(reposition_button)); |
| |
| auto exit_button_label = CreateControllerLabel( |
| kControllerExitButtonLabel, kControllerExitButtonOffset, |
| l10n_util::GetStringUTF16(IDS_VR_BUTTON_EXIT), model, element_binding); |
| exit_button_label->AddBinding( |
| VR_BIND_FUNC(bool, Model, model, model->fullscreen_enabled(), UiElement, |
| exit_button_label.get(), SetVisible)); |
| callout_group->AddChild(std::move(exit_button_label)); |
| |
| auto back_button_label = CreateControllerLabel( |
| kControllerBackButtonLabel, kControllerBackButtonOffset, |
| l10n_util::GetStringUTF16(IDS_VR_BUTTON_BACK), model, element_binding); |
| back_button_label->AddBinding(VR_BIND_FUNC( |
| bool, Model, model, |
| model->omnibox_editing_enabled() || model->voice_search_enabled(), |
| UiElement, back_button_label.get(), SetVisible)); |
| callout_group->AddChild(std::move(back_button_label)); |
| |
| auto reposition_finish_button = CreateControllerLabel( |
| kControllerRepositionFinishLabel, kControllerBackButtonOffset, |
| l10n_util::GetStringUTF16(IDS_VR_BUTTON_APP_REPOSITION), model, |
| element_binding); |
| reposition_finish_button->AddBinding( |
| VR_BIND_FUNC(bool, Model, model, model->reposition_window_enabled(), |
| UiElement, reposition_finish_button.get(), SetVisible)); |
| callout_group->AddChild(std::move(reposition_finish_button)); |
| |
| controller->AddChild(std::move(callout_group)); |
| |
| scene->AddUiElement(kControllerGroup, std::move(controller)); |
| } |
| |
| void OnControllerModelRemoved(UiScene* scene, ControllerBinding* binding) { |
| scene->RemoveUiElement(binding->view()->id()); |
| } |
| |
| 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->location_bar_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->location_bar_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(GlTextureLocation, 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; |
| } |
| |
| } // 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(); |
| CreateWebVrSubtree(); |
| CreateKeyboard(); |
| CreateControllers(); |
| } |
| |
| 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->primary_controller().laser_direction, Repositioner, |
| repositioner.get(), set_laser_direction)); |
| repositioner->AddBinding(VR_BIND( |
| bool, Model, model_, model->primary_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::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->primary_controller().touchpad_touch_position, Resizer, |
| resizer.get(), set_touch_position)); |
| resizer->AddBinding(VR_BIND_FUNC( |
| bool, Model, model_, model->primary_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(GlTextureLocation, 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( |
| GlTextureLocation, 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::CreateExternalPromptNotifcationOverlay() { |
| #if !defined(OS_ANDROID) |
| auto phase = kPhaseForeground; |
| auto icon = Create<VectorIcon>(kNone, phase, 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); |
| VectorIcon* vector_icon = icon.get(); |
| |
| auto text1 = Create<Text>(kNone, phase, kPromptFontSize); |
| text1->SetType(kTypePromptText); |
| text1->SetLayoutMode(kMultiLineFixedWidth); |
| text1->SetAlignment(kTextAlignmentLeft); |
| text1->SetFieldWidth(kPromptTextWidth); |
| VR_BIND_COLOR(model_, text1.get(), &ColorScheme::modal_prompt_foreground, |
| &Text::SetColor); |
| Text* line1_text = text1.get(); |
| |
| auto text2 = Create<Text>(kNone, phase, kPromptFontSize); |
| text2->SetType(kTypePromptText); |
| text2->SetLayoutMode(kMultiLineFixedWidth); |
| text2->SetAlignment(kTextAlignmentLeft); |
| text2->SetFieldWidth(kPromptTextWidth); |
| text2->SetText(l10n_util::GetStringUTF16(IDS_DESKTOP_PROMPT_DOFF_HEADSET)); |
| VR_BIND_COLOR(model_, text2.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_spacer1 = CreateSpacer(0, 0); |
| text_spacer1->set_bounds_contain_children(true); |
| text_spacer1->set_padding(0, (kPromptIconSize - kPromptFontSize) / 2, 0, 0); |
| text_spacer1->AddChild(std::move(text1)); |
| |
| // The second spacer gives space between the two strings. |
| auto text_spacer2 = CreateSpacer(0, 0); |
| text_spacer2->set_bounds_contain_children(true); |
| text_spacer2->set_padding(0, kPromptFontSize * 2, 0, 0); |
| text_spacer2->AddChild(std::move(text2)); |
| |
| // Two lines of text: |
| auto text_layout = |
| Create<LinearLayout>(kNone, kPhaseNone, LinearLayout::kDown); |
| text_layout->AddChild(std::move(text_spacer1)); |
| text_layout->AddChild(std::move(text_spacer2)); |
| |
| // Contents of the message box. |
| 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_layout)); |
| |
| auto prompt_window = Create<Rect>(kNone, phase); |
| prompt_window->SetType(kTypePromptBackground); |
| prompt_window->set_bounds_contain_children(true); |
| prompt_window->set_hit_testable(false); |
| prompt_window->set_padding(kPromptPadding, kPromptPadding); |
| prompt_window->SetTranslate(0, 0, kPromptShadowOffsetDMM); |
| prompt_window->set_corner_radius(kPromptCornerRadius); |
| prompt_window->AddChild(std::move(message_layout)); |
| VR_BIND_COLOR(model_, prompt_window.get(), |
| &ColorScheme::modal_prompt_background, &Rect::SetColor); |
| |
| auto scaler = Create<ScaledDepthAdjuster>(kNone, kPhaseNone, kPromptDistance); |
| scaler->SetType(kTypeScaledDepthAdjuster); |
| scaler->AddChild(std::move(prompt_window)); |
| scaler->set_contributes_to_parent_bounds(false); |
| VR_BIND_VISIBILITY(scaler, model->web_vr_enabled() && |
| (model->web_vr.external_prompt_notification != |
| ExternalPromptNotificationType::kPromptNone)); |
| |
| scaler->AddBinding(std::make_unique< |
| Binding<std::tuple<ExternalPromptNotificationType, GURL>>>( |
| VR_BIND_LAMBDA( |
| [](Model* m) { |
| return std::tuple<ExternalPromptNotificationType, GURL>( |
| m->web_vr.external_prompt_notification, |
| m->location_bar_state.gurl); |
| }, |
| base::Unretained(model_)), |
| VR_BIND_LAMBDA( |
| [](Text* text_element, VectorIcon* icon_element, |
| const std::tuple<ExternalPromptNotificationType, GURL>& |
| prompt_data) { |
| ExternalPromptNotificationType prompt = std::get<0>(prompt_data); |
| if (prompt == ExternalPromptNotificationType::kPromptNone) |
| return; |
| |
| int message_id = 0; |
| const gfx::VectorIcon* icon = nullptr; |
| switch (prompt) { |
| case ExternalPromptNotificationType::kPromptAudio: |
| message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_INFOBAR_TEXT; |
| icon = &vector_icons::kMicIcon; |
| break; |
| case ExternalPromptNotificationType::kPromptBluetooth: |
| message_id = IDS_VR_DESKTOP_BLUETOOTH_PROMPT; |
| icon = &kBluetoothIcon; |
| break; |
| case ExternalPromptNotificationType::kPromptNone: |
| NOTREACHED(); |
| } |
| |
| const GURL& gurl = std::get<1>(prompt_data); |
| base::string16 url_text = |
| url_formatter::FormatUrlForSecurityDisplay( |
| gurl.GetOrigin(), |
| url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC); |
| |
| text_element->SetText( |
| l10n_util::GetStringFUTF16(message_id, url_text)); |
| icon_element->SetIcon(icon); |
| }, |
| base::Unretained(line1_text), base::Unretained(vector_icon)))); |
| |
| scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(scaler)); |
| #endif // !defined(OS_ANDROID) |
| } |
| |
| void UiSceneCreator::CreateWebVrSubtree() { |
| CreateWebVrOverlayElements(); |
| CreateWebVrTimeoutScreen(); |
| CreateExternalPromptNotifcationOverlay(); |
| |
| // 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.IsImmersiveWebXrVisible() || |
| model->web_vr.showing_hosted_ui || |
| model->web_vr.external_prompt_notification != |
| ExternalPromptNotificationType::kPromptNone)); |
| 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::CreateControllers() { |
| 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 = Create<UiElement>(kNone, kPhaseNone); |
| group->SetName(kControllerGroup); |
| |
| // Set up the vector binding to manage controllers dynamically. |
| ControllerSetBinding::ModelAddedCallback added_callback = |
| base::BindRepeating(&OnControllerModelAdded, base::Unretained(scene_), |
| base::Unretained(model_)); |
| ControllerSetBinding::ModelRemovedCallback removed_callback = |
| base::BindRepeating(&OnControllerModelRemoved, base::Unretained(scene_)); |
| |
| group->AddBinding(std::make_unique<ControllerSetBinding>( |
| &model_->controllers, added_callback, removed_callback)); |
| |
| scene_->AddUiElement(kControllerRoot, std::move(group)); |
| |
| 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->primary_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->primary_controller().touching_touchpad, |
| m->primary_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->location_bar_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->location_bar_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_)), |
| gfx::kNoneIcon, 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->location_bar_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->location_bar_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->location_bar_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)); |
| |
| 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)); |
| } |
| |
| int new_incognito_tab_res_id = IDS_VR_MENU_NEW_PRIVATE_TAB; |
| int close_incognito_tabs_res_id = IDS_VR_MENU_CLOSE_PRIVATE_TABS; |
| if (!model_->use_new_incognito_strings) { |
| new_incognito_tab_res_id = IDS_VR_MENU_NEW_INCOGNITO_TAB; |
| close_incognito_tabs_res_id = IDS_VR_MENU_CLOSE_INCOGNITO_TABS; |
| } |
| |
| struct MenuItem { |
| UiElementName name; |
| int string_id; |
| base::RepeatingCallback<void(UiBrowserInterface*)> action; |
| base::RepeatingCallback<bool(Model*)> visibility; |
| }; |
| 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_open; })}, |
| {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->location_bar_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.IsImmersiveWebXrVisible() && |
| 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)); |
| } |
| |
| } // namespace vr |