blob: 44806ec71bd2e92f1ba6e28855a5743e300b5a45 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// 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 <numbers>
#include <string>
#include <tuple>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/vr/databinding/binding.h"
#include "chrome/browser/vr/elements/draw_phase.h"
#include "chrome/browser/vr/elements/environment/grid.h"
#include "chrome/browser/vr/elements/full_screen_rect.h"
#include "chrome/browser/vr/elements/indicator_spec.h"
#include "chrome/browser/vr/elements/linear_layout.h"
#include "chrome/browser/vr/elements/rect.h"
#include "chrome/browser/vr/elements/scaled_depth_adjuster.h"
#include "chrome/browser/vr/elements/text.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/vector_icon.h"
#include "chrome/browser/vr/elements/viewport_aware_root.h"
#include "chrome/browser/vr/model/model.h"
#include "chrome/browser/vr/target_property.h"
#include "chrome/browser/vr/ui.h"
#include "chrome/browser/vr/ui_scene.h"
#include "chrome/browser/vr/ui_scene_constants.h"
#include "chrome/grit/generated_resources.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "device/base/features.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/animation/keyframe/animation_curve.h"
#include "ui/gfx/animation/keyframe/keyframed_animation_curve.h"
#include "ui/gfx/geometry/transform_util.h"
#include "ui/gfx/paint_vector_icon.h"
#if BUILDFLAG(IS_WIN)
#include "chrome/browser/vr/elements/spinner.h"
#endif
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)
#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;
}
std::unique_ptr<TransientElement> CreateTransientParent(UiElementName name,
int timeout_seconds,
bool animate_opacity) {
auto element =
std::make_unique<SimpleTransientElement>(base::Seconds(timeout_seconds));
element->SetName(name);
element->SetVisible(false);
if (animate_opacity)
element->SetTransitionedProperties({OPACITY});
return element;
}
#if !BUILDFLAG(IS_ANDROID)
std::unique_ptr<UiElement> CreateSpacer(float width, float height) {
auto spacer = Create<UiElement>(kNone, kPhaseNone);
spacer->SetType(kTypeSpacer);
spacer->SetSize(width, height);
return spacer;
}
#endif
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,
IndicatorSpec spec,
DrawPhase phase) {
auto container = Create<Rect>(spec.webvr_name, phase);
VR_BIND_COLOR(model, container.get(),
&ColorScheme::webvr_permission_background, &Rect::SetColor);
container->SetCornerRadius(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, phase, 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);
icon_element->SetIcon(*spec.icon);
std::unique_ptr<UiElement> description_element;
auto text_element = Create<Text>(kNone, phase, kWebVrPermissionFontHeight);
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<Grid> CreateGrid(Model* model, UiElementName name) {
auto grid = Create<Grid>(name, kPhaseBackground);
grid->set_gridline_count(kFloorGridlineCount);
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, -std::numbers::pi_v<float> / 2);
}
void SetVisibleInLayout(UiElement* e, bool v) {
e->SetVisible(v);
e->set_requires_layout(v);
}
#if BUILDFLAG(IS_WIN)
void BindIndicatorTranscienceForWin(
TransientElement* e,
Model* model,
UiScene* scene,
const std::optional<
std::tuple<bool, CapturingStateModel, CapturingStateModel>>& last_value,
const std::tuple<bool, CapturingStateModel, CapturingStateModel>& value) {
const bool in_web_vr_presentation = model->web_vr.IsImmersiveWebXrVisible() &&
model->web_vr.has_received_permissions;
const CapturingStateModel active_capture = std::get<1>(value);
const CapturingStateModel potential_capture = std::get<2>(value);
if (!in_web_vr_presentation) {
e->SetVisibleImmediately(false);
return;
}
e->SetVisible(true);
e->RefreshVisible();
for (const auto& spec : GetIndicatorSpecs()) {
SetVisibleInLayout(
scene->GetUiElementByName(spec.webvr_name),
active_capture.*spec.signal || potential_capture.*spec.signal);
}
e->RemoveKeyframeModels(TRANSFORM);
e->SetTranslate(0, kWebVrPermissionOffsetStart, 0);
// Build up a keyframe model for the initial transition.
std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve(
gfx::KeyframedTransformAnimationCurve::Create());
gfx::TransformOperations value_1;
value_1.AppendTranslate(0, kWebVrPermissionOffsetStart, 0);
curve->AddKeyframe(gfx::TransformKeyframe::Create(
base::TimeDelta(), value_1,
gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE)));
gfx::TransformOperations value_2;
value_2.AppendTranslate(0, kWebVrPermissionOffsetOvershoot, 0);
curve->AddKeyframe(gfx::TransformKeyframe::Create(
base::Milliseconds(kWebVrPermissionOffsetMs), value_2,
gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE)));
gfx::TransformOperations value_3;
value_3.AppendTranslate(0, kWebVrPermissionOffsetFinal, 0);
curve->AddKeyframe(gfx::TransformKeyframe::Create(
base::Milliseconds(kWebVrPermissionAnimationDurationMs), value_3,
gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE)));
curve->set_target(e);
e->AddKeyframeModel(gfx::KeyframeModel::Create(
std::move(curve), gfx::KeyframeEffect::GetNextKeyframeModelId(),
TRANSFORM));
}
#else
void BindIndicatorTranscience(TransientElement* e,
Model* model,
UiScene* scene,
const bool& in_web_vr_presentation) {
if (!in_web_vr_presentation) {
e->SetVisibleImmediately(false);
return;
}
e->SetVisible(true);
e->RefreshVisible();
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);
e->SetTranslate(0, kWebVrPermissionOffsetStart, 0);
// Build up a keyframe model for the initial transition.
std::unique_ptr<gfx::KeyframedTransformAnimationCurve> curve(
gfx::KeyframedTransformAnimationCurve::Create());
gfx::TransformOperations value_1;
value_1.AppendTranslate(0, kWebVrPermissionOffsetStart, 0);
curve->AddKeyframe(gfx::TransformKeyframe::Create(
base::TimeDelta(), value_1,
gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE)));
gfx::TransformOperations value_2;
value_2.AppendTranslate(0, kWebVrPermissionOffsetOvershoot, 0);
curve->AddKeyframe(gfx::TransformKeyframe::Create(
base::Milliseconds(kWebVrPermissionOffsetMs), value_2,
gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE)));
gfx::TransformOperations value_3;
value_3.AppendTranslate(0, kWebVrPermissionOffsetFinal, 0);
curve->AddKeyframe(gfx::TransformKeyframe::Create(
base::Milliseconds(kWebVrPermissionAnimationDurationMs), value_3,
gfx::CubicBezierTimingFunction::CreatePreset(
gfx::CubicBezierTimingFunction::EaseType::EASE)));
curve->set_target(e);
e->AddKeyframeModel(gfx::KeyframeModel::Create(
std::move(curve), gfx::KeyframeEffect::GetNextKeyframeModelId(),
TRANSFORM));
}
#endif
int GetIndicatorsTimeout() {
// Some runtimes on Windows have quite lengthy animations that may cause
// indicators to not be visible at our normal timeout length.
#if BUILDFLAG(IS_WIN)
return kWindowsInitialIndicatorsTimeoutSeconds;
#else
return kToastTimeoutSeconds;
#endif
}
} // namespace
UiSceneCreator::UiSceneCreator(UiScene* scene, Ui* ui, Model* model)
: scene_(scene), ui_(ui), model_(model) {}
UiSceneCreator::~UiSceneCreator() = default;
void UiSceneCreator::CreateScene() {
CreateWebVrRoot();
CreateViewportAwareRoot();
CreateWebVrSubtree();
}
void UiSceneCreator::CreateWebVrRoot() {
auto element = std::make_unique<UiElement>();
element->SetName(kWebVrRoot);
element->SetVisible(true);
element->SetTranslate(0, 0, 0);
scene_->AddUiElement(kRoot, std::move(element));
}
void UiSceneCreator::CreateExternalPromptNotifcationOverlay() {
#if !BUILDFLAG(IS_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->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->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_padding(kPromptPadding, kPromptPadding);
prompt_window->SetTranslate(0, 0, kPromptShadowOffsetDMM);
prompt_window->SetCornerRadius(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->SetName(kWebXrExternalPromptNotification);
scaler->SetType(kTypeScaledDepthAdjuster);
scaler->AddChild(std::move(prompt_window));
scaler->set_contributes_to_parent_bounds(false);
VR_BIND_VISIBILITY(scaler, (model->web_vr.external_prompt_notification !=
ExternalPromptNotificationType::kPromptNone));
scaler->AddBinding(std::make_unique<Binding<ExternalPromptNotificationType>>(
VR_BIND_LAMBDA(
[](Model* m) { return m->web_vr.external_prompt_notification; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](Text* text_element, VectorIcon* icon_element,
const ExternalPromptNotificationType& prompt) {
if (prompt == ExternalPromptNotificationType::kPromptNone)
return;
int message_id = 0;
const gfx::VectorIcon* icon = nullptr;
switch (prompt) {
case ExternalPromptNotificationType::kPromptGenericPermission:
message_id = IDS_VR_DESKTOP_GENERIC_PERMISSION_PROMPT;
icon = &kOpenInBrowserIcon;
break;
case ExternalPromptNotificationType::kPromptNone:
NOTREACHED();
}
text_element->SetText(l10n_util::GetStringUTF16(message_id));
icon_element->SetIcon(icon);
},
base::Unretained(line1_text), base::Unretained(vector_icon))));
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(scaler));
#endif // !BUILDFLAG(IS_ANDROID)
}
void UiSceneCreator::CreateWebVrSubtree() {
CreateWebVrOverlayElements();
CreateWebVrTimeoutScreen();
CreateExternalPromptNotifcationOverlay();
// 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.IsImmersiveWebXrVisible() ||
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(
[](UiElement* timeout_screen) {
return timeout_screen->GetTargetOpacity() != 0.f;
},
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()))));
// TODO(https://crbug.com/327467653): Investigate spinner code.
#if BUILDFLAG(IS_WIN)
auto spinner = std::make_unique<Spinner>(512);
spinner->SetName(kWebVrTimeoutSpinner);
spinner->SetDrawPhase(kPhaseForeground);
spinner->SetTransitionedProperties({OPACITY});
spinner->SetVisible(false);
spinner->SetSize(kTimeoutSpinnerSizeDMM, kTimeoutSpinnerSizeDMM);
spinner->SetTranslate(0, kTimeoutSpinnerVerticalOffsetDMM, 0);
spinner->SetColor(model_->color_scheme().web_vr_timeout_spinner);
VR_BIND_VISIBILITY(spinner, model->web_vr.state == kWebVrTimeoutImminent);
#endif
auto timeout_message = Create<Rect>(kWebVrTimeoutMessage, kPhaseForeground);
timeout_message->SetVisible(false);
timeout_message->set_bounds_contain_children(true);
timeout_message->SetCornerRadius(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->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->SetFieldWidth(kTimeoutMessageTextWidthDMM);
timeout_layout->AddChild(std::move(timeout_icon));
timeout_layout->AddChild(std::move(timeout_text));
timeout_message->AddChild(std::move(timeout_layout));
scaler->AddChild(std::move(timeout_message));
#if BUILDFLAG(IS_WIN)
scaler->AddChild(std::move(spinner));
#endif
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(scaler));
}
void UiSceneCreator::CreateViewportAwareRoot() {
auto element = std::make_unique<ViewportAwareRoot>();
element->SetName(kWebVrViewportAwareRoot);
// On Windows, allow the viewport-aware UI to translate as well as rotate, so
// it remains centered appropriately if the user moves. Only enabled for
// OS_WIN, since it conflicts with browser UI that isn't shown on Windows.
#if BUILDFLAG(IS_WIN)
element->SetRecenterOnRotate(true);
#endif
scene_->AddUiElement(kWebVrRoot, std::move(element));
}
void UiSceneCreator::CreateWebVrOverlayElements() {
// Create transient WebVR elements.
auto indicators = Create<LinearLayout>(kWebVrIndicatorLayout, kPhaseNone,
LinearLayout::kDown);
indicators->SetTranslate(0, 0, kWebVrPermissionDepth);
indicators->set_margin(kWebVrPermissionOuterMargin);
DrawPhase phase = kPhaseOverlayForeground;
auto specs = GetIndicatorSpecs();
for (const auto& spec : specs) {
indicators->AddChild(CreateWebVrIndicator(model_, spec, phase));
}
auto parent = CreateTransientParent(kWebVrIndicatorTransience,
GetIndicatorsTimeout(), true);
#if BUILDFLAG(IS_WIN)
parent->AddBinding(
std::make_unique<
Binding<std::tuple<bool, CapturingStateModel, CapturingStateModel>>>(
VR_BIND_LAMBDA(
[](Model* model) {
return std::tuple<bool, CapturingStateModel,
CapturingStateModel>(
model->web_vr.IsImmersiveWebXrVisible() &&
model->web_vr.has_received_permissions,
model->active_capturing, model->potential_capturing);
},
base::Unretained(model_)),
VR_BIND_LAMBDA(BindIndicatorTranscienceForWin,
base::Unretained(parent.get()),
base::Unretained(model_), base::Unretained(scene_))));
#else
parent->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA(
[](Model* model) {
return model->web_vr.IsImmersiveWebXrVisible() &&
model->web_vr.has_received_permissions;
},
base::Unretained(model_)),
VR_BIND_LAMBDA(BindIndicatorTranscience, base::Unretained(parent.get()),
base::Unretained(model_), base::Unretained(scene_))));
#endif
auto scaler = std::make_unique<ScaledDepthAdjuster>(kWebVrToastDistance);
scaler->AddChild(std::move(indicators));
parent->AddChild(std::move(scaler));
scene_->AddUiElement(kWebVrViewportAwareRoot, std::move(parent));
}
} // namespace vr