blob: 36f73630a0e2d24888f7b45088816b99e0ac3859 [file] [log] [blame]
// Copyright 2020 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/system/holding_space/holding_space_tray_bubble.h"
#include <vector>
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_item.h"
#include "ash/public/cpp/holding_space/holding_space_metrics.h"
#include "ash/public/cpp/holding_space/holding_space_prefs.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/system/holding_space/holding_space_item_view.h"
#include "ash/system/holding_space/holding_space_tray.h"
#include "ash/system/holding_space/pinned_files_bubble.h"
#include "ash/system/holding_space/recent_files_bubble.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_utils.h"
#include "ash/wm/work_area_insets.h"
#include "ui/aura/window.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// Helpers ---------------------------------------------------------------------
// Finds all visible `HoldingSpaceItem`s in `parent`'s view hierarchy.
void FindVisibleHoldingSpaceItems(
views::View* parent,
std::vector<const HoldingSpaceItem*>* result) {
for (views::View* view : parent->children()) {
if (view->GetVisible() && HoldingSpaceItemView::IsInstance(view))
result->push_back(HoldingSpaceItemView::Cast(view)->item());
FindVisibleHoldingSpaceItems(view, result);
}
}
// Records the time from first availability to first entry into holding space.
void RecordTimeFromFirstAvailabilityToFirstEntry(PrefService* prefs) {
base::Time time_of_first_availability =
holding_space_prefs::GetTimeOfFirstAvailability(prefs).value();
base::Time time_of_first_entry =
holding_space_prefs::GetTimeOfFirstEntry(prefs).value();
holding_space_metrics::RecordTimeFromFirstAvailabilityToFirstEntry(
time_of_first_entry - time_of_first_availability);
}
// HoldingSpaceBubbleContainer -------------------------------------------------
class HoldingSpaceBubbleContainer : public views::View {
public:
HoldingSpaceBubbleContainer() {
layout_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
kHoldingSpaceBubbleContainerChildSpacing));
}
void SetFlexForChild(views::View* child, int flex) {
layout_->SetFlexForView(child, flex);
}
private:
// views::View:
void ChildPreferredSizeChanged(views::View* child) override {
if (GetWidget())
PreferredSizeChanged();
}
void ChildVisibilityChanged(views::View* child) override {
if (GetWidget())
PreferredSizeChanged();
}
views::BoxLayout* layout_ = nullptr;
};
} // namespace
// HoldingSpaceTrayBubble ------------------------------------------------------
HoldingSpaceTrayBubble::HoldingSpaceTrayBubble(
HoldingSpaceTray* holding_space_tray,
bool show_by_click)
: holding_space_tray_(holding_space_tray) {
TrayBubbleView::InitParams init_params;
init_params.delegate = holding_space_tray;
init_params.parent_window = holding_space_tray->GetBubbleWindowContainer();
init_params.anchor_view = nullptr;
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.anchor_rect =
holding_space_tray->shelf()->GetSystemTrayAnchorRect();
init_params.insets = GetTrayBubbleInsets();
init_params.shelf_alignment = holding_space_tray->shelf()->alignment();
init_params.preferred_width = kHoldingSpaceBubbleWidth;
init_params.close_on_deactivate = true;
init_params.show_by_click = show_by_click;
init_params.has_shadow = false;
// Create and customize bubble view.
TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
bubble_view->SetMaxHeight(CalculateMaxHeight());
HoldingSpaceBubbleContainer* bubble_container = bubble_view->AddChildView(
std::make_unique<HoldingSpaceBubbleContainer>());
// Add pinned files child bubble.
child_bubbles_.push_back(bubble_container->AddChildView(
std::make_unique<PinnedFilesBubble>(&delegate_)));
bubble_container->SetFlexForChild(child_bubbles_.back(), 1);
// Add recent files child bubble.
child_bubbles_.push_back(bubble_container->AddChildView(
std::make_unique<RecentFilesBubble>(&delegate_)));
// Initialize child bubbles.
for (HoldingSpaceTrayChildBubble* child_bubble : child_bubbles_)
child_bubble->Init();
// Show the bubble.
bubble_wrapper_ = std::make_unique<TrayBubbleWrapper>(
holding_space_tray, bubble_view, false /* is_persistent */);
// Set bubble frame to be invisible.
bubble_wrapper_->GetBubbleWidget()
->non_client_view()
->frame_view()
->SetVisible(false);
PrefService* const prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
// Mark when holding space was first entered. If this is not the first entry
// into holding space, this will no-op. If this is the first entry, record the
// amount of time from first availability to first entry into holding space.
if (holding_space_prefs::MarkTimeOfFirstEntry(prefs))
RecordTimeFromFirstAvailabilityToFirstEntry(prefs);
// Record visible holding space items.
std::vector<const HoldingSpaceItem*> visible_items;
FindVisibleHoldingSpaceItems(bubble_view, &visible_items);
holding_space_metrics::RecordItemCounts(visible_items);
shelf_observer_.Add(holding_space_tray_->shelf());
tablet_mode_observer_.Add(Shell::Get()->tablet_mode_controller());
}
HoldingSpaceTrayBubble::~HoldingSpaceTrayBubble() {
bubble_wrapper_->bubble_view()->ResetDelegate();
// Explicitly reset child bubbles so that they will stop observing the holding
// space controller/model while they are asynchronously destroyed.
for (HoldingSpaceTrayChildBubble* child_bubble : child_bubbles_)
child_bubble->Reset();
}
void HoldingSpaceTrayBubble::AnchorUpdated() {
bubble_wrapper_->bubble_view()->UpdateBubble();
}
TrayBubbleView* HoldingSpaceTrayBubble::GetBubbleView() {
return bubble_wrapper_->bubble_view();
}
views::Widget* HoldingSpaceTrayBubble::GetBubbleWidget() {
return bubble_wrapper_->GetBubbleWidget();
}
int HoldingSpaceTrayBubble::CalculateMaxHeight() const {
const WorkAreaInsets* work_area = WorkAreaInsets::ForWindow(
holding_space_tray_->shelf()->GetWindow()->GetRootWindow());
const int bottom =
holding_space_tray_->shelf()->IsHorizontalAlignment()
? holding_space_tray_->shelf()->GetShelfBoundsInScreen().y()
: work_area->user_work_area_bounds().bottom();
const int free_space_height_above_anchor =
bottom - work_area->user_work_area_bounds().y();
const gfx::Insets insets = GetTrayBubbleInsets();
const int bubble_vertical_margin = insets.top() + insets.bottom();
return free_space_height_above_anchor - bubble_vertical_margin;
}
void HoldingSpaceTrayBubble::UpdateBubbleBounds() {
bubble_wrapper_->bubble_view()->SetMaxHeight(CalculateMaxHeight());
bubble_wrapper_->bubble_view()->ChangeAnchorRect(
holding_space_tray_->shelf()->GetSystemTrayAnchorRect());
}
void HoldingSpaceTrayBubble::OnDisplayConfigurationChanged() {
UpdateBubbleBounds();
}
void HoldingSpaceTrayBubble::OnAutoHideStateChanged(ShelfAutoHideState state) {
UpdateBubbleBounds();
}
void HoldingSpaceTrayBubble::OnTabletModeStarted() {
UpdateBubbleBounds();
}
void HoldingSpaceTrayBubble::OnTabletModeEnded() {
UpdateBubbleBounds();
}
} // namespace ash