blob: 1adcb820ca9b6daf23bb950f5b7cc2ce1f8b3ff7 [file] [log] [blame]
// Copyright 2019 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 "ash/app_list/views/assistant/assistant_page_view.h"
#include <memory>
#include <utility>
#include "ash/app_list/app_list_view_delegate.h"
#include "ash/app_list/views/assistant/assistant_main_view.h"
#include "ash/app_list/views/contents_view.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_web_view.h"
#include "ash/assistant/util/assistant_util.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/chromeos/search_box/search_box_constants.h"
#include "ui/views/background.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/layout/fill_layout.h"
namespace app_list {
namespace {
// The height of the search box in |search_result_page_view_|. It is only for
// animation.
constexpr int kSearchBoxHeightDip = 56;
// The shadow elevation value for the shadow of the Assistant search box.
constexpr int kShadowElevation = 12;
} // namespace
AssistantPageView::AssistantPageView(
ash::AssistantViewDelegate* assistant_view_delegate)
: assistant_view_delegate_(assistant_view_delegate) {
InitLayout();
// |assistant_view_delegate_| could be nullptr in test.
if (assistant_view_delegate_)
assistant_view_delegate_->AddUiModelObserver(this);
}
AssistantPageView::~AssistantPageView() {
if (assistant_view_delegate_)
assistant_view_delegate_->RemoveUiModelObserver(this);
}
void AssistantPageView::InitLayout() {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
// Create and set a shadow to be displayed as a border for this view.
auto shadow_border = std::make_unique<views::BubbleBorder>(
views::BubbleBorder::NONE, views::BubbleBorder::SMALL_SHADOW,
SK_ColorWHITE);
shadow_border->SetCornerRadius(
search_box::kSearchBoxBorderCornerRadiusSearchResult);
shadow_border->set_md_shadow_elevation(kShadowElevation);
SetBorder(std::move(shadow_border));
SetBackground(views::CreateBackgroundFromPainter(
views::Painter::CreateSolidRoundRectPainter(
SK_ColorWHITE, search_box::kSearchBoxBorderCornerRadiusSearchResult,
border()->GetInsets())));
SetLayoutManager(std::make_unique<views::FillLayout>());
if (assistant_view_delegate_) {
assistant_main_view_ = new AssistantMainView(assistant_view_delegate_);
AddChildView(assistant_main_view_);
// Web view.
assistant_web_view_ = new ash::AssistantWebView(assistant_view_delegate_);
AddChildView(assistant_web_view_);
// Update the view state based on the current UI mode.
OnUiModeChanged(assistant_view_delegate_->GetUiModel()->ui_mode(),
/*due_to_interaction=*/false);
}
}
const char* AssistantPageView::GetClassName() const {
return "AssistantPageView";
}
gfx::Size AssistantPageView::CalculatePreferredSize() const {
return gfx::Size(ash::kPreferredWidthDip, ash::kMaxHeightEmbeddedDip);
}
void AssistantPageView::RequestFocus() {
if (!assistant_view_delegate_)
return;
switch (assistant_view_delegate_->GetUiModel()->ui_mode()) {
case ash::AssistantUiMode::kLauncherEmbeddedUi:
if (assistant_main_view_)
assistant_main_view_->RequestFocus();
break;
case ash::AssistantUiMode::kWebUi:
if (assistant_web_view_)
assistant_web_view_->RequestFocus();
break;
case ash::AssistantUiMode::kMainUi:
case ash::AssistantUiMode::kMiniUi:
NOTREACHED();
break;
}
}
void AssistantPageView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
View::GetAccessibleNodeData(node_data);
node_data->SetName(l10n_util::GetStringUTF16(IDS_ASH_ASSISTANT_WINDOW));
}
void AssistantPageView::OnMouseEvent(ui::MouseEvent* event) {
switch (event->type()) {
case ui::ET_MOUSE_PRESSED:
// Prevents closing the AppListView when a click event is not handled.
event->StopPropagation();
break;
default:
break;
}
}
void AssistantPageView::OnGestureEvent(ui::GestureEvent* event) {
switch (event->type()) {
case ui::ET_GESTURE_TAP:
case ui::ET_GESTURE_DOUBLE_TAP:
case ui::ET_GESTURE_LONG_PRESS:
case ui::ET_GESTURE_LONG_TAP:
case ui::ET_GESTURE_TWO_FINGER_TAP:
// Prevents closing the AppListView when a tap event is not handled.
event->StopPropagation();
break;
default:
break;
}
}
gfx::Rect AssistantPageView::GetPageBoundsForState(
ash::AppListState state) const {
gfx::Rect bounds;
if (state != ash::AppListState::kStateEmbeddedAssistant) {
// Hides this view behind the search box by using the same bounds.
bounds = AppListPage::contents_view()->GetSearchBoxBoundsForState(state);
} else {
bounds = AppListPage::GetSearchBoxBounds();
bounds.Offset((bounds.width() - ash::kPreferredWidthDip) / 2, 0);
bounds.set_size(GetPreferredSize());
}
return AddShadowBorderToBounds(bounds);
}
gfx::Rect AssistantPageView::GetSearchBoxBounds() const {
gfx::Rect bounds(AppListPage::GetSearchBoxBounds());
bounds.Offset((bounds.width() - ash::kPreferredWidthDip) / 2, 0);
bounds.set_size(gfx::Size(ash::kPreferredWidthDip, kSearchBoxHeightDip));
return bounds;
}
views::View* AssistantPageView::GetFirstFocusableView() {
return GetFocusManager()->GetNextFocusableView(
this, GetWidget(), /*reverse=*/false, /*dont_loop=*/false);
}
views::View* AssistantPageView::GetLastFocusableView() {
return GetFocusManager()->GetNextFocusableView(
this, GetWidget(), /*reverse=*/true, /*dont_loop=*/false);
}
void AssistantPageView::OnUiModeChanged(ash::AssistantUiMode ui_mode,
bool due_to_interaction) {
for (auto* child : children())
child->SetVisible(false);
switch (ui_mode) {
case ash::AssistantUiMode::kLauncherEmbeddedUi:
if (assistant_main_view_)
assistant_main_view_->SetVisible(true);
break;
case ash::AssistantUiMode::kWebUi:
if (assistant_web_view_)
assistant_web_view_->SetVisible(true);
break;
case ash::AssistantUiMode::kMainUi:
case ash::AssistantUiMode::kMiniUi:
NOTREACHED();
break;
}
PreferredSizeChanged();
RequestFocus();
}
void AssistantPageView::OnUiVisibilityChanged(
ash::AssistantVisibility new_visibility,
ash::AssistantVisibility old_visibility,
base::Optional<ash::AssistantEntryPoint> entry_point,
base::Optional<ash::AssistantExitPoint> exit_point) {
if (!assistant_view_delegate_)
return;
if (new_visibility != ash::AssistantVisibility::kVisible)
return;
const bool prefer_voice = assistant_view_delegate_->IsTabletMode() ||
assistant_view_delegate_->IsLaunchWithMicOpen();
if (!ash::assistant::util::IsVoiceEntryPoint(entry_point.value(),
prefer_voice)) {
NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
}
}
gfx::Rect AssistantPageView::AddShadowBorderToBounds(
const gfx::Rect& bounds) const {
gfx::Rect new_bounds(bounds);
new_bounds.Inset(-border()->GetInsets());
return new_bounds;
}
} // namespace app_list