chromium/ash/app_list/views/assistant/assistant_main_view.cc

// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/app_list/views/assistant/assistant_main_view.h"

#include <algorithm>
#include <memory>

#include "ash/app_list/views/assistant/assistant_dialog_plate.h"
#include "ash/app_list/views/assistant/assistant_main_stage.h"
#include "ash/assistant/model/assistant_ui_model.h"
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/assistant_view_delegate.h"
#include "ash/assistant/ui/assistant_view_ids.h"
#include "ash/assistant/util/animation_util.h"
#include "ash/assistant/util/assistant_util.h"
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
#include "ash/search_box/search_box_constants.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/views/layout/box_layout.h"

namespace ash {

namespace {

// Dialog plate animation.
constexpr base::TimeDelta kDialogPlateAnimationFadeInDelay =
    base::Milliseconds(283);
constexpr base::TimeDelta kDialogPlateAnimationFadeInDuration =
    base::Milliseconds(167);

}  // namespace

AssistantMainView::AssistantMainView(AssistantViewDelegate* delegate)
    : delegate_(delegate) {
  SetID(AssistantViewID::kMainView);
  InitLayout();

  assistant_controller_observation_.Observe(AssistantController::Get());
  AssistantUiController::Get()->GetModel()->AddObserver(this);
}

AssistantMainView::~AssistantMainView() {
  if (AssistantUiController::Get())
    AssistantUiController::Get()->GetModel()->RemoveObserver(this);
}

void AssistantMainView::ChildPreferredSizeChanged(views::View* child) {
  PreferredSizeChanged();

  // Even though the preferred size for |main_stage_| may change, its bounds
  // may not actually change due to height restrictions imposed by its parent.
  // For this reason, we need to explicitly trigger a layout pass so that the
  // children of |main_stage_| are properly updated.
  if (child == main_stage_) {
    DeprecatedLayoutImmediately();
    SchedulePaint();
  }
}

void AssistantMainView::ChildVisibilityChanged(views::View* child) {
  PreferredSizeChanged();
}

views::View* AssistantMainView::FindFirstFocusableView() {
  // In those instances in which we want to override views::FocusSearch
  // behavior, DialogPlate will identify the first focusable view.
  return dialog_plate_->FindFirstFocusableView();
}

void AssistantMainView::RequestFocus() {
  dialog_plate_->RequestFocus();
}

void AssistantMainView::OnAssistantControllerDestroying() {
  AssistantUiController::Get()->GetModel()->RemoveObserver(this);
  DCHECK(assistant_controller_observation_.IsObservingSource(
      AssistantController::Get()));
  assistant_controller_observation_.Reset();
}

void AssistantMainView::OnUiVisibilityChanged(
    AssistantVisibility new_visibility,
    AssistantVisibility old_visibility,
    std::optional<AssistantEntryPoint> entry_point,
    std::optional<AssistantExitPoint> exit_point) {
  if (!assistant::util::IsStartingSession(new_visibility, old_visibility)) {
    return;
  }

  // When Assistant is starting a new session, we animate in the appearance of
  // the dialog plate.
  using assistant::util::CreateLayerAnimationSequence;
  using assistant::util::CreateOpacityElement;

  // Animate the dialog plate from 0% to 100% opacity with delay.
  dialog_plate_->layer()->SetOpacity(0.f);
  dialog_plate_->layer()->GetAnimator()->StartAnimation(
      CreateLayerAnimationSequence(
          ui::LayerAnimationElement::CreatePauseElement(
              ui::LayerAnimationElement::AnimatableProperty::OPACITY,
              kDialogPlateAnimationFadeInDelay),
          CreateOpacityElement(1.f, kDialogPlateAnimationFadeInDuration)));
}

void AssistantMainView::InitLayout() {
  constexpr int radius = kSearchBoxBorderCornerRadiusSearchResult;

  SetPaintToLayer();
  layer()->SetFillsBoundsOpaquely(false);
  layer()->SetRoundedCornerRadius({radius, radius, radius, radius});

  views::BoxLayout* layout =
      SetLayoutManager(std::make_unique<views::BoxLayout>(
          views::BoxLayout::Orientation::kVertical));
  layout->set_cross_axis_alignment(
      views::BoxLayout::CrossAxisAlignment::kCenter);

  // Dialog plate, which will be animated on its own layer.
  dialog_plate_ =
      AddChildView(std::make_unique<AssistantDialogPlate>(delegate_));
  dialog_plate_->SetPaintToLayer();
  dialog_plate_->layer()->SetFillsBoundsOpaquely(false);

  // Main stage.
  main_stage_ =
      AddChildView(std::make_unique<AppListAssistantMainStage>(delegate_));

  layout->SetFlexForView(main_stage_, 1);
}

BEGIN_METADATA(AssistantMainView)
END_METADATA

}  // namespace ash