blob: 1c1258601ca5c247c91d0d158e90677348494a02 [file] [log] [blame]
// Copyright (c) 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/public/cpp/shelf_config.h"
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/style/ash_color_provider.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "chromeos/constants/chromeos_switches.h"
#include "ui/gfx/color_analysis.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/color_utils.h"
namespace ash {
namespace {
// When any edge of the primary display is less than or equal to this threshold,
// dense shelf will be active.
const int kDenseShelfScreenSizeThreshold = 600;
// Returns whether tablet mode is currently active.
bool IsTabletMode() {
return Shell::Get()->tablet_mode_controller() &&
Shell::Get()->tablet_mode_controller()->InTabletMode();
}
} // namespace
ShelfConfig::ShelfConfig()
: is_dense_(false),
is_app_list_visible_(false),
shelf_button_icon_size_(44),
shelf_button_icon_size_dense_(36),
shelf_button_size_(56),
shelf_button_size_dense_(48),
shelf_button_spacing_(8),
shelf_status_area_hit_region_padding_(4),
shelf_status_area_hit_region_padding_dense_(2),
app_icon_group_margin_(16),
shelf_control_permanent_highlight_background_(
SkColorSetA(SK_ColorWHITE, 26)), // 10%
shelf_focus_border_color_(gfx::kGoogleBlue300),
workspace_area_visible_inset_(2),
workspace_area_auto_hide_inset_(5),
hidden_shelf_in_screen_portion_(3),
shelf_ink_drop_base_color_(SK_ColorWHITE),
shelf_ink_drop_visible_opacity_(0.2f),
shelf_icon_color_(SK_ColorWHITE),
status_indicator_offset_from_shelf_edge_(1),
scrollable_shelf_ripple_padding_(2),
shelf_tooltip_preview_height_(128),
shelf_tooltip_preview_max_width_(192),
shelf_tooltip_preview_max_ratio_(1.5), // = 3/2
shelf_tooltip_preview_min_ratio_(0.666), // = 2/3
shelf_blur_radius_(30),
mousewheel_scroll_offset_threshold_(20) {
UpdateIsDense();
}
ShelfConfig::~ShelfConfig() = default;
// static
ShelfConfig* ShelfConfig::Get() {
return Shell::Get()->shelf_config();
}
void ShelfConfig::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ShelfConfig::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ShelfConfig::Init() {
if (!chromeos::switches::ShouldShowShelfHotseat())
return;
Shell* shell = Shell::Get();
shell->tablet_mode_controller()->AddObserver(this);
shell->app_list_controller()->AddObserver(this);
display::Screen::GetScreen()->AddObserver(this);
}
void ShelfConfig::Shutdown() {
if (!chromeos::switches::ShouldShowShelfHotseat())
return;
Shell* shell = Shell::Get();
display::Screen::GetScreen()->RemoveObserver(this);
shell->app_list_controller()->RemoveObserver(this);
shell->tablet_mode_controller()->RemoveObserver(this);
}
void ShelfConfig::OnTabletModeStarted() {
UpdateIsDense();
}
void ShelfConfig::OnTabletModeEnded() {
UpdateIsDense();
}
void ShelfConfig::OnDisplayMetricsChanged(const display::Display& display,
uint32_t changed_metrics) {
UpdateIsDense();
}
void ShelfConfig::OnAppListVisibilityWillChange(bool shown,
int64_t display_id) {
// Let's check that the app visibility mechanism isn't mis-firing, which
// would lead to a lot of extraneous relayout work.
DCHECK_NE(is_app_list_visible_, shown);
is_app_list_visible_ = shown;
OnShelfConfigUpdated();
}
int ShelfConfig::shelf_size() const {
return GetShelfSize(false /*ignore_in_app_state*/);
}
int ShelfConfig::in_app_shelf_size() const {
return is_dense_ ? 36 : 40;
}
int ShelfConfig::system_shelf_size() const {
return GetShelfSize(true /*ignore_in_app_state*/);
}
int ShelfConfig::hotseat_size() const {
if (!chromeos::switches::ShouldShowShelfHotseat() || !IsTabletMode()) {
return shelf_size();
}
return is_dense_ ? 48 : 56;
}
int ShelfConfig::hotseat_bottom_padding() const {
return 8;
}
int ShelfConfig::button_size() const {
return is_dense_ ? shelf_button_size_dense_ : shelf_button_size_;
}
int ShelfConfig::button_spacing() const {
return shelf_button_spacing_;
}
int ShelfConfig::button_icon_size() const {
return is_dense_ ? shelf_button_icon_size_dense_ : shelf_button_icon_size_;
}
int ShelfConfig::control_size() const {
if (!chromeos::switches::ShouldShowShelfHotseat())
return 40;
if (!IsTabletMode())
return 36;
return is_dense_ ? 36 : 40;
}
int ShelfConfig::control_border_radius() const {
return (chromeos::switches::ShouldShowShelfHotseat() && is_in_app() &&
IsTabletMode())
? 0
: control_size() / 2;
}
int ShelfConfig::overflow_button_margin() const {
return (button_size() - control_size()) / 2;
}
int ShelfConfig::home_button_edge_spacing() const {
return (shelf_size() - control_size()) / 2;
}
base::TimeDelta ShelfConfig::hotseat_background_animation_duration() const {
// This matches the duration of the maximize/minimize animation.
return base::TimeDelta::FromMilliseconds(300);
}
base::TimeDelta ShelfConfig::shelf_animation_duration() const {
if (chromeos::switches::ShouldShowShelfHotseat())
return hotseat_background_animation_duration();
return base::TimeDelta::FromMilliseconds(200);
}
int ShelfConfig::status_area_hit_region_padding() const {
return is_dense_ ? shelf_status_area_hit_region_padding_dense_
: shelf_status_area_hit_region_padding_;
}
bool ShelfConfig::is_in_app() const {
Shell* shell = Shell::Get();
const auto* session = shell->session_controller();
if (!session)
return false;
return session->GetSessionState() == session_manager::SessionState::ACTIVE &&
!is_app_list_visible_;
}
void ShelfConfig::UpdateIsDense() {
const gfx::Rect screen_size =
display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
const bool new_is_dense =
chromeos::switches::ShouldShowShelfHotseat() &&
(!IsTabletMode() ||
(screen_size.width() <= kDenseShelfScreenSizeThreshold ||
screen_size.height() <= kDenseShelfScreenSizeThreshold));
if (new_is_dense == is_dense_)
return;
is_dense_ = new_is_dense;
OnShelfConfigUpdated();
}
int ShelfConfig::GetShelfSize(bool ignore_in_app_state) const {
// Before the hotseat redesign, the shelf always has the same size.
if (!chromeos::switches::ShouldShowShelfHotseat())
return 56;
// In clamshell mode, the shelf always has the same size.
if (!IsTabletMode())
return 48;
if (!ignore_in_app_state && is_in_app())
return in_app_shelf_size();
return is_dense_ ? 48 : 56;
}
SkColor ShelfConfig::GetShelfControlButtonColor() const {
if (chromeos::switches::ShouldShowShelfHotseat() && IsTabletMode() &&
Shell::Get()->session_controller()->GetSessionState() ==
session_manager::SessionState::ACTIVE) {
return is_in_app() ? SK_ColorTRANSPARENT : GetDefaultShelfColor();
}
return shelf_control_permanent_highlight_background_;
}
SkColor ShelfConfig::GetShelfWithAppListColor() const {
return SkColorSetA(SK_ColorBLACK, 20); // 8% opacity
}
SkColor ShelfConfig::GetMaximizedShelfColor() const {
// Using 0xFF causes clipping on the overlay candidate content, which prevent
// HW overlay, probably due to a bug in compositor. Fix it and use 0xFF.
// crbug.com/901538
return SkColorSetA(GetDefaultShelfColor(), 254); // ~100% opacity
}
SkColor ShelfConfig::GetDefaultShelfColor() const {
if (!features::IsBackgroundBlurEnabled()) {
return AshColorProvider::Get()->GetBaseLayerColor(
AshColorProvider::BaseLayerType::kTransparent90,
AshColorProvider::AshColorMode::kDark);
}
SkColor final_color = AshColorProvider::Get()->GetBaseLayerColor(
IsTabletMode() ? AshColorProvider::BaseLayerType::kTransparent60
: AshColorProvider::BaseLayerType::kTransparent74,
AshColorProvider::AshColorMode::kDark);
int final_alpha = SkColorGetA(final_color);
if (!Shell::Get()->wallpaper_controller())
return final_color;
SkColor dark_muted_color =
Shell::Get()->wallpaper_controller()->GetProminentColor(
color_utils::ColorProfile(color_utils::LumaRange::DARK,
color_utils::SaturationRange::MUTED));
if (dark_muted_color == kInvalidWallpaperColor)
return final_color;
// Combine SK_ColorBLACK at 50% opacity with |dark_muted_color|.
final_color = color_utils::GetResultingPaintColor(
SkColorSetA(SK_ColorBLACK, 127), dark_muted_color);
return SkColorSetA(final_color, final_alpha);
}
int ShelfConfig::GetShelfControlButtonBlurRadius() const {
if (features::IsBackgroundBlurEnabled() &&
chromeos::switches::ShouldShowShelfHotseat() && IsTabletMode() &&
!is_in_app()) {
return shelf_blur_radius_;
}
return 0;
}
void ShelfConfig::OnShelfConfigUpdated() {
for (auto& observer : observers_)
observer.OnShelfConfigUpdated();
}
} // namespace ash